Android
Android
#android
Table of Contents
About 1
Remarks 2
Versions 2
Examples 3
Change/add theme 4
Compiling Apps 4
Basic Configuration 4
Add an activity 9
Declaring a resource 19
See also 20
Application Fundamentals 20
App Components 20
Context 21
Remarks 28
Examples 28
Basic spinner 29
Examples 31
Inserting data 31
Updating data 31
Examples 32
Chapter 5: ACRA 35
Syntax 35
Parameters 35
Remarks 35
Examples 35
ACRAHandler 35
Example manifest 36
Installation 36
Chapter 6: Activity 37
Introduction 37
Syntax 37
Parameters 38
Remarks 38
Examples 38
Activity launchMode 42
Standard: 43
SingleTop: 43
SingleTask: 43
SingleInstance: 43
Examples 44
Introduction 48
Examples 48
Introduction 53
Remarks 53
Examples 53
Example Output 53
Rooted device 58
When you have a rooted device but don't have access to a USB cable 58
Avoid timeout 59
Reboot device 59
Connect device by IP 60
Start/stop adb 61
View logcat 61
Taking a screenshot and video (for kitkat only) from a device display 63
Video 64
Sending broadcast 64
Backup 66
List all permissions that require runtime grant from users on Android 6.0 67
Introduction 69
Syntax 69
Parameters 69
Examples 69
Send text, key pressed and touch events to Android Device via ADB 69
List packages 71
Introduction 77
Examples 77
Syntax 86
Parameters 86
Remarks 86
Examples 86
Implementing 86
Manifest 86
XML 87
Java 87
Introduction 89
Examples 89
AIDL Service 89
Examples 91
Introduction 94
Examples 94
Room peristence 96
Custom LiveData 98
Examples 101
Introduction 104
Remarks 104
Examples 104
Introduction 111
Examples 111
How to call functions in a native library via the JNI interface 111
Examples 114
Examples 118
Remarks 121
Examples 121
Examples 123
Introduction 129
Remarks 129
Examples 129
Examples 135
Examples 138
Examples 150
Remarks 152
Examples 153
Examples 155
Introduction 157
Examples 157
Introduction 164
Examples 164
Examples 167
ValueAnimator 169
ObjectAnimator 170
ViewPropertyAnimator 171
Introduction 173
Examples 173
Parameters 176
Examples 176
Example 176
Usage: 177
Note 177
Note 179
THREAD_POOL_EXECUTOR 185
SERIAL_EXECUTOR 185
Examples 188
Examples 189
Remarks 190
Examples 190
strings.xml 191
MainActivity.java 191
Introduction 195
Examples 195
Granularity 195
Remarks 197
Examples 197
Introduction 199
Syntax 199
Parameters 199
Examples 199
Remarks 201
Examples 201
Permissions 201
Introduction 210
Examples 210
Introduction 216
Remarks 216
Examples 216
Introduction 227
Remarks 227
Links: 227
Examples 227
Introduction 231
Examples 231
Examples 239
Introduction 242
Remarks 242
ButterKnife 242
Examples 242
Configuring ButterKnife in your project 242
Syntax 250
Examples 250
Using the same click event for one or more Views in the XML 251
Examples 258
Parameters 259
Remarks 259
Examples 260
Preview the main camera in a TextureView 260
Examples 269
AndroidManifest.xml 269
How to start camera or gallery and save camera result to storage 274
Decode bitmap correctly rotated from the uri fetched with the intent 277
Remarks 281
Examples 281
Examples 287
Introduction 290
Parameters 290
Remarks 291
Examples 291
Introduction 298
Syntax 298
Parameters 298
Remarks 298
Examples 298
Introduction 303
Remarks 303
Examples 303
Examples 304
Introduction 305
Syntax 305
Parameters 305
Remarks 306
Examples 306
Chains 307
Chapter 58: ConstraintSet 309
Introduction 309
Examples 309
Remarks 310
Examples 310
Introduction 314
Syntax 314
Remarks 314
Examples 314
Examples 316
example: 316
Introduction 317
Remarks 317
Examples 317
Parameters 321
Remarks 321
Examples 321
Remarks 324
Examples 324
Examples 333
Introduction 335
Syntax 335
Parameters 335
Remarks 335
Examples 336
Examples 338
build.gradle 347
Examples 351
Remarks 353
Examples 353
Chapter 70: Creating your own libraries for Android applications 358
Examples 358
Examples 361
Syntax 366
Remarks 366
Examples 366
Remarks 373
Examples 373
Introduction 385
Examples 385
Examples 387
Examples 393
Examples 397
Chapter 78: Define step value (increment) for custom RangeSeekBar 399
Introduction 399
Remarks 399
Examples 400
Introduction 401
Examples 401
Examples 403
Installation 404
Examples 405
Parameters 406
Remarks 406
Examples 406
DatePickerDialog 409
DatePicker 410
Examples 417
Remarks 420
Examples 422
Examples 424
Examples 428
Icon or button inside Custom Edit Text and its action and click listeners. 433
Examples 436
Remarks 437
Examples 437
Introduction 443
Examples 443
Remarks 444
Examples 444
Examples 449
NetworkOnMainThreadException 449
ActivityNotFoundException 450
OutOfMemoryError 450
DexException 451
UncaughtException 451
Examples 454
Main steps to play video & audio using the standard TrackRenderer implementations 455
Syntax 456
Parameters 456
Examples 456
Introduction 462
Examples 462
Introduction 464
Syntax 464
Examples 464
Remarks 467
Examples 467
Fastfile lane to build and install all flavors for given build type to a device 469
Introduction 471
Remarks 471
Examples 471
Examples 475
Specify the directories in which the files you want to share are placed 475
Remarks 477
Examples 477
Introduction 488
Remarks 488
Firebase - Extended documentation: 488
Examples 488
Remarks 516
Examples 518
Introduction 522
Examples 522
This code that i have implemnted in my app for pushing image,message and also link for ope 523
Examples 526
Remarks 528
Examples 528
Designing and understanding how to retrieve realtime data from the Firebase Database 529
Example 532
Introduction 539
Parameters 539
Remarks 539
Examples 540
Introduction 547
Examples 547
Examples 548
Introduction 549
Syntax 549
Remarks 550
Constructor 550
Examples 550
Navigation between fragments using backstack and static fabric pattern 551
Example 553
Introduction 560
Remarks 560
Examples 560
Introduction 563
Examples 563
Remarks 565
Examples 565
Remarks 568
Examples 568
Introduction 570
Examples 570
Chapter 114: Getting system font names and using the fonts 573
Introduction 573
Examples 573
Remarks 574
Examples 574
ImageView 575
Remarks 581
Examples 581
Get changes for location within a certain range using Fence API 584
Introduction 586
Remarks 586
Examples 586
Parameters 600
Remarks 600
Examples 600
Do not launch Google Maps when the map is clicked (lite mode) 621
UISettings 621
Examples 626
Open Google Play Store with the list of all applications from your publisher account 626
Introduction 628
Examples 628
Introduction 630
Syntax 630
Remarks 630
Examples 631
Plugins 632
Dependencies 632
signingConfig 633
BuildConfigField 636
ResValue 637
Why are there two build.gradle files in an Android Studio project? 640
Specifying different application IDs for build types and product flavors 642
Enable experimental NDK plugin support for Gradle and AndroidStudio 647
Introduction 656
Examples 656
Creating an Entity with GreenDAO 3.X that has a Composite Primary Key 658
Syntax 662
Parameters 662
Examples 662
Introduction 665
Syntax 665
Examples 666
Remarks 678
Examples 678
Introduction 682
Parameters 682
Remarks 682
Resources 683
Examples 683
Introduction 687
Parameters 687
Examples 687
Buttons 687
Surface 688
Introduction 691
Examples 691
PTT_KEY 691
YELLOW_KEY 691
SOS_KEY 691
GREEN_KEY 691
Examples 693
Introduction 697
Remarks 697
Examples 698
Syntax 700
Remarks 700
Examples 700
Usage 710
Examples 711
Introduction 714
Syntax 714
Parameters 714
Examples 714
MLRoundedImageView.java 723
Syntax 725
Parameters 725
Remarks 725
Examples 725
Examples 727
Consumable In-app Purchases 727
Step 1: 727
Step 2: 727
Step 3: 727
Step 4: 727
Step 5: 728
Step 6: 731
Examples 734
Remarks 735
Examples 735
Syntax 739
Parameters 739
Examples 739
Remarks 742
Examples 742
Instructions 742
Introduction 751
Syntax 751
Parameters 752
Remarks 752
Examples 753
OriginActivity 753
DestinationActivity 754
MainActivity: 756
DetailActivity: 757
Parcelable 768
Serializable 770
Syntax 773
Remarks 773
Examples 773
Syntax 776
Remarks 776
Examples 776
Prepare your project and write the first UIAutomator test 776
Examples 779
Introduction 783
Remarks 783
Examples 783
Introduction 788
Examples 788
Introduction 790
Examples 790
Examples 793
Examples 794
Part III: Create a Jenkins Job for your Android project 795
Remarks 797
Examples 797
Syntax 800
Remarks 800
Examples 800
Examples 809
Hide keyboard when user taps anywhere else on the screen 809
Introduction 811
Syntax 811
Remarks 811
Performance impact from using RelativeLayouts near the top of your view hierarchy 812
Examples 813
LinearLayout 813
RelativeLayout 814
GridLayout 819
FrameLayout 822
CoordinatorLayout 823
LayoutParams 829
Introduction 833
Remarks 833
Examples 833
Introduction 834
Remarks 834
Examples 835
Remarks 837
Examples 837
Introduction 841
Remarks 841
Examples 841
Introduction 845
Parameters 845
Remarks 845
Examples 846
Reloading 848
Introduction 850
Syntax 850
Examples 850
Load the Image from Resource from Android Device. Using Intents. 850
Examples 853
Currency 853
Configuration types and qualifier names for each folder under the "res" directory 855
Exhaustive list of all different configuration types and their qualifier values for androi 855
Examples 863
Introduction 865
Remarks 865
LocationManager 865
FusedLocationProviderApi 866
TroubleShooting 868
Examples 875
Syntax 888
Parameters 888
Remarks 888
Definition 888
Examples 889
Logging 891
Performance: 893
Security: 893
Conclusion: 893
Introduction 899
Examples 899
Remarks 900
Examples 900
Introduction 902
Remarks 902
Examples 902
RippleDrawable 910
Syntax 923
Remarks 923
Examples 925
Syntax 932
Remarks 932
Examples 932
Examples 935
Fetch Audio/MP3 files from specific folder of device or fetch all files 935
Avoid memory leaks with Anonymous Class, Handler, Timer Task, Thread 948
Syntax 950
Parameters 950
Remarks 950
Examples 950
Step 1: 951
Step 2: 952
Introduction 955
Remarks 955
Examples 955
JSON into Java 955
Introduction 957
Remarks 957
Examples 958
Introduction 962
Remarks 962
Examples 962
Notes: 966
ILoginPresenter.class 970
LoginPresenterCompl.class 970
UserModel.class 971
IUser.class 972
MVP 972
Remarks 974
Examples 975
Remarks 983
Examples 983
Introduction 992
Syntax 992
Parameters 992
Examples 992
Examples 998
Here is what it looks like on Android Marshmallow with the Heads Up Notification: 999
Here is what it looks like on Android KitKat with the Ticker: 1000
Dynamically getting the correct pixel size for the large icon 1006
Examples 1008
Examples 1012
Introduction 1014
Examples 1014
Remarks 1026
Examples 1026
Problem: 1030
Solution: 1030
Example: 1030
MainActivity: 1030
AsyncTaskLoader: 1031
Note: 1031
Examples 1033
Remarks 1038
Examples 1038
Examples 1040
Retrieve application version 1040
Introduction 1043
Examples 1043
MainActivity.java 1043
Introduction 1048
Examples 1048
Introduction 1051
Remarks 1051
Examples 1051
Introduction 1055
Examples 1055
Introduction 1056
Remarks 1056
Examples 1056
Adding Picasso Library to your Android Project 1056
Gradle. 1056
Maven: 1056
Try offline disk cache first, then go online and fetch the image 1062
Introduction 1064
Examples 1064
Examples 1065
Introduction 1067
Remarks 1067
Examples 1068
Remarks 1070
Examples 1070
Indeterminate 1077
Determinate 1077
Buffer 1078
Examples 1081
Introduction 1087
Parameters 1087
Remarks 1087
Examples 1087
Chapter 196: Publish .aar file to Apache Archiva with Gradle 1089
Examples 1089
Examples 1091
Examples 1093
Introduction 1095
Remarks 1095
Examples 1095
try-with-resources 1098
Models 1101
Introduction 1104
Parameters 1104
Remarks 1104
Examples 1105
Show default view till items load or when data is not available 1121
Examples 1126
Connect the RecyclerView with the PlaceListAdapter and the dataset 1133
Done! 1134
StaggeredGridLayoutManager 1134
Syntax 1137
Parameters 1137
Remarks 1137
Examples 1137
Examples 1144
Introduction 1155
Examples 1155
Kernels 1158
Basics 1160
Conclusion 1163
Blur an image 1163
BlurBitmapTask.java 1165
Usage: 1167
Examples 1168
9 Patches 1179
Introduction 1186
Remarks 1186
Examples 1186
Examples 1204
Examples 1209
Introduction 1213
Examples 1213
Configuration 1213
Introduction 1215
Remarks 1215
Examples 1216
Android 6.0 multiple permissions 1216
Include all permission-related code to an abstract base class and extend the activity of t 1221
Examples 1224
Introduction 1230
Syntax 1230
Parameters 1230
Remarks 1230
Examples 1230
Introduction 1232
Syntax 1232
Parameters 1232
Remarks 1232
Examples 1232
Examples 1234
Examples 1235
Introduction 1238
Remarks 1238
Examples 1238
Introduction 1246
Syntax 1246
Examples 1246
Introduction 1249
Syntax 1249
Parameters 1250
Remarks 1250
Examples 1250
Examples 1265
Introduction 1266
Examples 1266
Examples 1269
Syntax 1272
Parameters 1272
Remarks 1272
Examples 1272
Syntax 1277
Examples 1277
Examples 1282
Examples 1285
Examples 1288
Introduction 1290
Remarks 1290
Examples 1290
Syntax 1307
Parameters 1307
Examples 1307
Using Internal Storage 1307
Chapter 229: Strict Mode Policy : A tool to catch the bug in the Compile Time. 1317
Introduction 1317
Remarks 1317
Examples 1317
The below Code Snippet is to setup the StrictMode for Thread Policies. This Code is to be 1317
The below code deals with leaks of memory, like it detects when in SQLLite finalize is cal 1317
Remarks 1318
Orientation 1318
Units 1318
px 1319
in 1319
mm 1319
pt 1319
dp or dip 1319
sp 1319
Examples 1320
Syntax 1323
Examples 1323
Introduction 1325
Examples 1325
Sync adapter with every min requesting value from server. 1325
Examples 1335
Introduction 1336
Remarks 1336
Examples 1336
Remarks 1338
Espresso 1338
Troubleshooting 1338
Examples 1338
Up Navigation 1345
Examples 1351
Introduction 1356
Remarks 1356
Examples 1356
TextInputEditText 1358
Introduction 1360
Syntax 1360
Remarks 1360
Examples 1360
Introduction 1373
Examples 1373
Examples 1375
Examples 1381
Examples 1383
GetCurrentRealTime 1384
Introduction 1386
Syntax 1386
Parameters 1386
Remarks 1386
Examples 1387
Remarks 1391
Examples 1391
Examples 1393
How to vary between child and parent view group touch events 1393
Examples 1397
Step 2: Add code for ImageView in your XML layout to display the above drawable. 1397
Step 3: Access the XML transition drawable in onCreate() method of your Activity and start 1397
Examples 1399
Remarks 1401
Examples 1401
Examples 1403
Remarks 1404
Examples 1404
Breakdown 1404
Setup 1408
Exceptions 1411
Remarks 1414
Examples 1414
Examples 1416
Introduction 1417
Parameters 1417
Remarks 1417
Examples 1418
VectorDrawable Usage Example 1418
Examples 1422
Using 1422
tags 1423
Examples 1429
Examples 1431
Introduction 1433
Examples 1433
Introduction 1435
Remarks 1435
Examples 1435
selected_dot.xml 1442
default_dot.xml 1442
tab_selector.xml 1443
Introduction 1445
Syntax 1445
Remarks 1445
Installation 1445
Examples 1446
Adding custom headers to your requests [e.g. for basic auth] 1448
Boolean variable response from server with json request in volley 1454
Introduction 1457
Remarks 1457
Examples 1457
Introduction 1462
Examples 1462
Remarks 1464
Examples 1464
Metadata 1464
Right on your Application ==> New ==> Widget ==> App Widget 1466
Examples 1468
Introduction 1472
Syntax 1472
Remarks 1472
Appium 1472
Parameters 1472
Examples 1473
IdlingResource 1475
Implementation 1475
NOTES 1476
Example 1476
Usage 1477
Chapter 265: XMPP register login and chat simple example 1479
Examples 1479
Examples 1488
Remarks 1491
Examples 1491
Examples 1501
Credits 1503
About
You can share this PDF with anyone you feel could benefit from it, downloaded the latest version
from: android
It is an unofficial and free Android ebook created for educational purposes. All the content is
extracted from Stack Overflow Documentation, which is written by many hardworking individuals at
Stack Overflow. It is neither affiliated with Stack Overflow nor official Android.
The content is released under Creative Commons BY-SA, and the list of contributors to each
chapter are provided in the credits section at the end of this book. Images may be copyright of
their respective owners unless otherwise specified. All trademarks and registered trademarks are
the property of their respective company owners.
Use the content presented in this book at your own risk; it is not guaranteed to be correct nor
accurate, please send your feedback and corrections to [email protected]
https://riptutorial.com/ 1
Chapter 1: Getting started with Android
Remarks
If you want to learn more about the Android Gradle Plugin setting check out the android-gradle
Documentation.
If you are interested in alternative emulators, you could look at Genymotion. It provides a free plan
and requires smaller amount of RAM.
Versions
https://riptutorial.com/ 2
Version API Level Version Code Release Date
Examples
Setting up Android Studio
Android Studio is the Android development IDE that is officially supported and recommended by
Google. Android Studio comes bundled with the Android SDK Manager, which is a tool to
download the Android SDK components required to start developing apps.
If you need to work on old projects that were built using older SDK versions, you may
need to download these versions as well
Since Android Studio 2.2, a copy of the latest OpenJDK comes bundled with the install and is the
recommended JDK (Java Development Kit) for all Android Studio projects. This removes the
requirement of having Oracle's JDK package installed. To use the bundled SDK, proceed as
follows;
1. Open your project in Android Studio and select File > Project Structure in the menu bar.
2. In the SDK Location page and under JDK location, check the Use embedded JDK
checkbox.
3. Click OK.
https://riptutorial.com/ 3
Configure Android Studio
Android Studio provides access to two configuration files through the Help menu:
• studio.vmoptions: Customize options for Studio's Java Virtual Machine (JVM), such as heap
size and cache size. Note that on Linux machines this file may be named
studio64.vmoptions, depending on your version of Android Studio.
• idea.properties: Customize Android Studio properties, such as the plugins folder path or
maximum supported file size.
Change/add theme
You can change it as your preference.File->Settings->Editor->Colors & Fonts-> and select a
theme.Also you can download new themes from http://color-themes.com/ Once you have
downloaded the .jar.zip file, go to File -> Import Settings... and choose the file downloaded.
Compiling Apps
Create a new project or open an existing project in Android Studio and press the green Play button
on the top toolbar to run it. If it is gray you need to wait a second to allow Android Studio to
properly index some files, the progress of which can be seen in the bottom status bar.
If you want to create a project from the shell make sure that you have a local.properties file,
which is created by Android Studio automatically. If you need to create the project without Android
Studio you need a line starting with sdk.dir= followed by the path to your SDK installation.
Open a shell and go into the project's directory. Enter ./gradlew aR and press enter. aR is a shortcut
for assembleRelease, which will download all dependencies for you and build the app. The final APK
file will be in ProjectName/ModuleName/build/outputs/apk and will been called ModuleName-release.apk.
Note: this guide is based on Android Studio 2.2, but the process on other versions is
mainly the same.
https://riptutorial.com/ 4
You can start a new project in two ways:
• Click Start a New Android Studio Project from the welcome screen.
• Navigate to File → New Project if you already have a project open.
Next, you need to describe your application by filling out some fields:
Example: Hello World. You can always change it later in AndroidManifest.xml file.
2. Company Domain - This is the qualifier for your project's package name.
Example: stackoverflow.com.
3. Package Name (aka applicationId) - This is the fully qualified project package name.
It should follow Reverse Domain Name Notation (aka Reverse DNS): Top Level Domain .
Company Domain . [Company Segment .] Application Name.
Don't use the default prefix "com.example" unless you don't intend to submit your
application to the Google Play Store. The package name will be your unique
applicationId in Google Play.
4. Project Location - This is the directory where your project will be stored.
https://riptutorial.com/ 5
https://riptutorial.com/ 6
Chart of the current Android version distributions, shown when you click Help me choose.
The Android Platform Distribution window shows the distribution of mobile devices running each
version of Android, as shown in Figure 2. Click on an API level to see a list of features introduced
in the corresponding version of Android. This helps you choose the minimum API Level that has all
the features that your apps needs, so you can reach as many devices as possible. Then click OK.
Now, choose what platforms and version of Android SDK the application will support.
https://riptutorial.com/ 7
https://riptutorial.com/ 8
uses to determine which devices an app can be installed on. For example, Stack Exchange's app
supports Android 4.1+.
Android Studio will tell you (approximately) what percentage of devices will be supported given the
specified minimum SDK.
Lower API levels target more devices but have fewer features available.
When deciding on the Minimum SDK, you should consider the Dashboards stats, which will give
you version information about the devices that visited the Google Play Store globally in the last
week.
Add an activity
Now we are going to select a default activity for our application. In Android, an Activity is a single
screen that will be presented to the user. An application can house multiple activities and navigate
between them. For this example, choose Empty Activity and click next.
Here, if you wish, you can change the name of the activity and layout. A good practice is to keep
Activity as a suffix for the activity name, and activity_ as a prefix for the layout name. If we leave
https://riptutorial.com/ 9
these as the default, Android Studio will generate an activity for us called MainActivity, and a
layout file called activity_main. Now click Finish.
Android Studio will create and configure our project, which can take some time depending on the
system.
On the left pane of Android Studio, we can see the structure of our Android application.
First, let's open AndroidManifest.xml by double clicking it. The Android manifest file describes some
of the basic information about an Android application. It contains the declaration of our activities,
as well as some more advanced components.
https://riptutorial.com/ 10
permissions defined by Android (listed in android.Manifest.permission) or declared by other
applications. Or it can define its own.
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Next, let's open activity_main.xml which is located in app/src/main/res/layout/. This file contains
declarations for the visual components of our MainActivity. You will see visual designer. This
allows you to drag and drop elements onto the selected layout.
You can also switch to the xml layout designer by clicking "Text" at the bottom of Android Studio,
as seen here:
https://riptutorial.com/ 11
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.stackexchange.docs.helloworld.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>
You will see a widget called a TextView inside of this layout, with the android:text property set to
"Hello World!". This is a block of text that will be shown to the user when they run the application.
Next, let's take a look at MainActivity. This is the Java code that has been generated for
MainActivity.
As defined in our Android manifest, MainActivity will launch by default when a user starts the
HelloWorld app.
android {
signingConfigs {
applicationName {
keyAlias 'applicationName'
keyPassword 'password'
storeFile file('../key/applicationName.jks')
storePassword 'anotherPassword'
}
}
compileSdkVersion 26
buildToolsVersion "26.0.0"
https://riptutorial.com/ 12
defaultConfig {
applicationId "com.stackexchange.docs.helloworld"
minSdkVersion 16
targetSdkVersion 26
versionCode 1
versionName "1.0"
signingConfig signingConfigs.applicationName
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:26.0.0'
}
This file contains information about the build and your app version, and you can also use it to add
dependencies to external libraries. For now, let's not make any changes.
It is advisable to always select the latest version available for the dependencies:
• buildToolsVersion: 26.0.0
• com.android.support:appcompat-v7: 26.0.0 (July 2017)
• firebase: 11.0.4 (August 2017)
compileSdkVersion
compileSdkVersion is your way to tell Gradle what version of the Android SDK to compile your app
with. Using the new Android SDK is a requirement to use any of the new APIs added in that level.
It should be emphasized that changing your compileSdkVersion does not change runtime behavior.
While new compiler warnings/errors may be present when changing your compileSdkVersion, your
compileSdkVersion is not included in your APK: it is purely used at compile time.
Therefore it is strongly recommended that you always compile with the latest SDK. You’ll get all
the benefits of new compilation checks on existing code, avoid newly deprecated APIs, and be
ready to use new APIs.
minSdkVersion
If compileSdkVersion sets the newest APIs available to you, minSdkVersion is the lower bound for
your app. The minSdkVersion is one of the signals the Google Play Store uses to determine which
of a user’s devices an app can be installed on.
It also plays an important role during development: by default lint runs against your project,
warning you when you use any APIs above your minSdkVersion, helping you avoid the runtime
issue of attempting to call an API that doesn’t exist. Checking the system version at runtime is a
https://riptutorial.com/ 13
common technique when using APIs only on newer platform versions.
targetSdkVersion
targetSdkVersionis the main way Android provides forward compatibility by not applying behavior
changes unless the targetSdkVersion is updated. This allows you to use new APIs prior to working
through the behavior changes. Updating to target the latest SDK should be a high priority for every
app. That doesn’t mean you have to use every new feature introduced nor should you blindly
update your targetSdkVersion without testing.
targetSDKVersion is the version of Android which is the upper-limit for the available tools. If
targetSDKVersion is less than 23, the app does not need to request permissions at runtime for an
instance, even if the app is being run on API 23+. TargetSDKVersion does not prevent android
versions above the picked Android version from running the app.
• A basic example
• Introduction to the Gradle plugin for android and the wrapper
• Introduction to the configuration of the build.gradle and the DSL methods
If Developer Options is not visible in the settings, navigate to About Phone and tap on the Build
Number seven times. This will enable Developer Options to show up in your settings.
You also might need to change build.gradle configuration to build on a version that your device
has.
https://riptutorial.com/ 14
click OK.
On devices running Android 4.4 (KitKat) and possibly higher, a pop-up will be shown to authorize
USB debugging. Click OK to accept.
The application will now install and run on your Android device or emulator.
Android Studio
Since in the above examples Gradle is used, the location of the generated APK file is: <Your
Project Location>/app/build/outputs/apk/app-debug.apk
IntelliJ
If you are a user of IntelliJ before switching to Studio, and are importing your IntelliJ project
directly, then nothing changed. The location of the output will be the same under:
out/production/...
Eclipse
If you are importing Android Eclipse project directly, do not do this! As soon as you have
dependencies in your project (jars or Library Projects), this will not work and your project will not
be properly setup. If you have no dependencies, then the apk would be under the same location
as you'd find it in Eclipse:
bin/...
https://riptutorial.com/ 15
This is a minimalist Hello World example that uses only the most basic Android tools.
This example assumes Linux. You may have to adjust the syntax for your own platform.
1. Install additional packages using the SDK manager. Don't use android update sdk --no-ui as
instructed in the bundled Readme.txt; it downloads some 30 GB of unnecessary files.
Instead use the interactive SDK manager android sdk to get the recommended minimum of
packages.
2. Append the following JDK and SDK directories to your execution PATH. This is optional, but
the instructions below assume it.
• JDK/bin
• SDK/platform-tools
• SDK/tools
• SDK/build-tools/LATEST (as installed in step 1)
3. Create an Android virtual device. Use the interactive AVD Manager (android avd). You might
have to fiddle a bit and search for advice; the on-site instructions aren't always helpful.
https://riptutorial.com/ 16
mkdir --parents src/dom/domain
touch src/dom/domain/SayingHello.java
Content:
package dom.domain;
import android.widget.TextView;
8. Add a manifest:
touch AndroidManifest.xml
Content:
<?xml version='1.0'?>
<manifest xmlns:a='http://schemas.android.com/apk/res/android'
package='dom.domain' a:versionCode='0' a:versionName='0'>
<application a:label='Saying hello'>
<activity a:name='dom.domain.SayingHello'>
<intent-filter>
<category a:name='android.intent.category.LAUNCHER'/>
<action a:name='android.intent.action.MAIN'/>
</intent-filter>
</activity>
</application>
</manifest>
mkdir res
aapt package -f \
-I SDK/platforms/android-API/android.jar \
https://riptutorial.com/ 17
-J src -m \
-M AndroidManifest.xml -S res -v
Resource declarations (described further below) are actually optional. Meantime the above
call does nothing if res/ is still empty.
javac \
-bootclasspath SDK/platforms/android-API/android.jar \
-classpath src -source 1.7 -target 1.7 \
src/dom/domain/*.java
You could replace steps 11 and 12 with a single call to Jack if you like; it can compile directly
from Java source (.java → .dex). But there are advantages to compiling with javac. It's a
better known, better documented and more widely applicable tool.
aapt package -f \
-F app.apkPart \
-I SDK/platforms/android-API/android.jar \
-M AndroidManifest.xml -S res -v
It warns, "THIS TOOL IS DEPRECATED. See --help for more information." If --help fails with
an ArrayIndexOutOfBoundsException, then instead pass no arguments:
https://riptutorial.com/ 18
java -classpath SDK/tools/lib/sdklib.jar \
com.android.sdklib.build.ApkBuilderMain
It explains that the CLI (ApkBuilderMain) is deprecated in favour of directly calling the Java
API (ApkBuilder). (If you know how to do that from the command line, please update this
example.)
That's all. That's what it takes to say hello using the basic Android tools.
Declaring a resource
This section is optional. Resource declarations aren't required for a simple "hello world" app. If
they aren't required for your app either, then you could streamline the build somewhat by omitting
step 10, and removing the reference to the res/ directory from step 13.
Otherwise, here's a brief example of how to declare a resource, and how to reference it.
mkdir res/values
touch res/values/values.xml
Content:
<?xml version='1.0'?>
<resources>
<string name='appLabel'>Saying hello</string>
</resources>
19. Reference the resource from the XML manifest. This is a declarative style of reference:
https://riptutorial.com/ 19
<!-- <application a:label='Saying hello'> -->
<application a:label='@string/appLabel'>
20. Reference the same resource from the Java source. This is an imperative reference:
21. Test the above modifications by rebuilding, reinstalling and re-running the app (steps 10-17).
See also
• original question - The original question that prompted this example
• working example - A working build script that uses the above commands
Application Fundamentals
Android Apps are written in Java. The Android SDK tools compile the code, data and resource
files into an APK (Android package). Generally, one APK file contains all the content of the app.
Each app runs on its own virtual machine(VM) so that app can run isolated from other apps.
Android system works with the principle of least privilege. Each app only has access to the
components which it requires to do its work, and no more. However, there are ways for an app to
share data with other apps, such as by sharing Linux user id between app, or apps can request
permission to access device data like SD card, contacts etc.
App Components
App components are the building blocks of an Android app. Each components plays a specific role
in an Android app which serves a distinct purpose and has distinct life-cycles(the flow of how and
when the component is created and destroyed). Here are the four types of app components:
1. Activities: An activity represents a single screen with a User Interface(UI). An Android app
may have more than one activity. (e.g. an email app might have one activity to list all the
emails, another to show the contents of each email, and another to compose new email.) All
the activities in an App work together to create a User eXperience (UX).
2. Services: A service runs in the background to perform long-running operations or to perform
work for a remote processes. A service does not provide any UI, it runs only in the
https://riptutorial.com/ 20
background with the User's input. (e.g. a service can play music in the background while the
user is in a different App, or it might download data from the internet without blocking user's
interaction with the Android device.)
3. Content Providers: A content provider manages shared app data. There are four ways to
store data in an app: it can be written to a file and stored in the file system, inserted or
updated to a SQLite database, posted to the web, or saved in any other persistent storage
location the App can access. Through content providers, other Apps can query or even
modify the data. (e.g. Android system provides a content provider that manages the user's
contact information so that any app which has permission can query the contacts.) Content
providers can also be used to save the data which is private to the app for better data
integrity.
4. Broadcast receivers: A broadcast receiver responds to the system-wide broadcasts of
announcements (e.g. a broadcast announcing that the screen has turned off, the battery is
low, etc.) or from Apps (e.g. to let other apps know that some data has been downloaded to
the device and is available for them to use). Broadcast receivers don't have UIs but they can
show notification in the status bar to alert the user. Usually broadcast receivers are used as
a gateway to other components of the app, consisting mostly of activities and services.
One unique aspect of the Android system is that any app can start another app's component (e.g.
if you want to make call, send SMS, open a web page, or view a photo, there is an app which
already does that and your app can make use of it, instead of developing a new activity for the
same task).
When the system starts a component, it starts the process for that app (if it isn't already running,
i.e. only one foreground process per app can run at any given time on an Android system) and
instantiates the classes needed for that component. Thus the component runs on the process of
that App that it belongs to. Therefore, unlike apps on other systems, Android apps don't have a
single entry point(there is no main() method).
Because the system runs each app in a separate process, one app cannot directly activate
another app's components, however the Android system can. Thus to start another app's
component, one app must send a message to the system that specifies an intent to start that
component, then the system will start that component.
Context
Instances of the class android.content.Context provide the connection to the Android system which
executes the application. Instance of Context is required to get access to the resources of the
project and the global information about the app's environment.
Let's have an easy to digest example: Consider you are in a hotel, and you want to eat something.
You call room-service and ask them to bring you things or clean up things for you. Now think of
this hotel as an Android app, yourself as an activity, and the room-service person is then your
context, which provides you access to the hotel resources like room-service, food items etc.
Yet an other example, You are in a restaurant sitting on a table, each table has an attendant,
when ever you want to order food items you ask the attendant to do so. The attendant then places
https://riptutorial.com/ 21
your order and your food items gets served on your table. Again in this example, the restaurant is
an Android App, the tables or the customers are App components, the food items are your App
resources and the attendant is your context thus giving you a way to access the resources like
food items.
Activating any of the above components requires the context's instance. Not just only the above,
but almost every system resource: creation of the UI using views(discussed later), creating
instance of system services, starting new activities or services -- all require context.
TL;DR It basically allows us to simulate real devices and test our apps without a real device.
an Android Virtual Device (AVD) definition lets you define the characteristics of an
Android Phone, Tablet, Android Wear, or Android TV device that you want to simulate
in the Android Emulator. The AVD Manager helps you easily create and manage
AVDs.
https://riptutorial.com/ 22
3. Now click the + Create Virtual Device... button. This will bring up Virtual Device Configuration
Dialog:
https://riptutorial.com/ 23
4. Select any device you want, then click Next:
https://riptutorial.com/ 24
5. Here you need to choose an Android version for your emulator. You might also need to
download it first by clicking Download. After you've chosen a version, click Next.
https://riptutorial.com/ 25
6. Here, enter a name for your emulator, initial orientation, and whether you want to display a
frame around it. After you chosen all these, click Finish.
7. Now you got a new AVD ready for launching your apps on it.
https://riptutorial.com/ 26
Read Getting started with Android online: https://riptutorial.com/android/topic/85/getting-started-
with-android
https://riptutorial.com/ 27
Chapter 2: 9-Patch Images
Remarks
A 9-patch image file is a specially formatted file so that Android knows which areas/portions of the
image can or cannot be scaled. It breaks your image into a 3x3 grid. The corners remain unscaled,
the sides are scaled in one direction and the center is scaled in both dimensions.
A Nine Patch (9-Patch) image is a bitmap that has a single pixel wide border around the entire
image. Ignoring the 4 pixels in the corners of the image. This border provides metadata for the
bitmap itself. Bounds are marked by solid black line(s).
The top border indicates areas that stretch horizontally. The left border indicates areas that stretch
vertically.
The bottom border indicates padding horizontally. The right border indicates padding vertically.
The padding borders are usually used to determine where text is to be drawn.
There is an excellent tool provided by Google that greatly simplifies the creation of these files.
Examples
Basic rounded corners
The top border controls horizontal stretching and the left border controls vertical stretching.
https://riptutorial.com/ 28
The parts of the image that are below the top border and to the right of the left border will expand
to fill all unused space.
Basic spinner
The Spinner can be reskinned according to your own style requirements using a Nine Patch.
The top border has only left of the icon marked. That indicates that I want the left side (complete
transparency) of the drawable to fill the Spinner view until the icon is reached.
The left border has marked transparent segments at the top and bottom of the icon marked. That
indicates that both the top and the bottom will expand to the size of the Spinner view. This will
leave the icon itself centered vertically.
https://riptutorial.com/ 29
Nine-patch images allow optional definition of the padding lines in the image. The padding lines
are the lines on the right and at the bottom.
If a View sets the 9-patch image as its background, the padding lines are used to define the space
for the View's content (e.g. the text input in an EditText). If the padding lines are not defined, the
left and top lines are used instead.
The content area of the stretched image then looks like this:
https://riptutorial.com/ 30
Chapter 3: Accessing SQLite databases
using the ContentValues class
Examples
Inserting and updating rows in a SQLite database
First, you need to open your SQLite database, which can be done as follows:
SQLiteDatabase myDataBase;
String mPath = dbhelper.DATABASE_PATH + dbhelper.DATABASE_NAME;
myDataBase = SQLiteDatabase.openDatabase(mPath, null, SQLiteDatabase.OPEN_READWRITE);
After opening the database, you can easily insert or update rows by using the ContentValues class.
The following examples assume that a first name is given by str_edtfname and a last nameby
str_edtlname. You also need to replace table_name by the name of your table that you want to
modify.
Inserting data
ContentValues values = new ContentValues();
values.put("First_Name", str_edtfname);
values.put("Last_Name", str_edtlname);
myDataBase.insert("table_name", null, values);
Updating data
ContentValues values = new ContentValues();
values.put("First_Name", str_edtfname);
values.put("Last_Name", str_edtlname);
myDataBase.update("table_name", values, "id" + " = ?", new String[] {id});
https://riptutorial.com/ 31
Chapter 4: Accounts and AccountManager
Examples
Understanding custom accounts/authentication
The following example is high level coverage of the key concepts and basic skeletal setup:-
1. Collects credentials from the user (Usually from a login screen you've created)
2. Authenticates the credentials with the server (stores custom authentication)
3. Stores the credentials on the device
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
String authTokenType, String[] requiredFeatures, Bundle options) {
//intent to start the login activity
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account,
Bundle options) {
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String
authTokenType,
Bundle options) throws NetworkErrorException {
//retrieve authentication tokens from account manager storage or custom storage or re-
authenticate old tokens and return new ones
}
@Override
public String getAuthTokenLabel(String authTokenType) {
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[]
features)
throws NetworkErrorException {
//check whether the account supports certain features
}
https://riptutorial.com/ 32
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
String authTokenType,
Bundle options) {
//when the user's session has expired or requires their previously available credentials
to be updated, here is the function to do it.
}
}
@Override
public void onCreate(){
authenticator = new AccountAuthenticator(this);
}
@Override
public IBinder onBind(Intent intent) {
return authenticator.getIBinder();
}
}
Authenticator XML configuration (The account manager framework requires. This is what you'll
see inside Settings -> Accounts in Android)
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="rename.with.your.applicationid"
android:icon="@drawable/app_icon"
android:label="@string/app_name"
android:smallIcon="@drawable/app_icon" />
Changes to the AndroidManifest.xml (Bring all the above concepts together to make it usable
programmatically through the AccountManager)
<application
...>
<service
android:name=".authenticator.AccountAuthenticatorService"
android:exported="false"
android:process=":authentication">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data
https://riptutorial.com/ 33
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator"/>
</service>
</application>
The next example will contain how to make use of this setup.
https://riptutorial.com/ 34
Chapter 5: ACRA
Syntax
• android:name=".ACRAHandler"
• ACRA.init(this, config);
• public class ACRAHandler extends Application {
Parameters
Parameter Description
Remarks
• ACRA no longer supports Google forms, so you need a backend:
https://github.com/ACRA/acra/wiki/Backends
Examples
ACRAHandler
@ReportsCrashes(
)
public class ACRAHandler extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
.build();
// Initialise ACRA
https://riptutorial.com/ 35
ACRA.init(this, config);
Example manifest
>
<!-- Internet is required. READ_LOGS are to ensure that the Logcat is transmitted-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_LOGS"/>
<application
android:allowBackup="true"
android:name=".ACRAHandler"<!-- Activates ACRA on startup -->
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
</manifest>
Installation
Maven
<dependency>
<groupId>ch.acra</groupId>
<artifactId>acra</artifactId>
<version>4.9.2</version>
<type>aar</type>
</dependency>
Gradle
compile 'ch.acra:acra:4.9.2'
https://riptutorial.com/ 36
Chapter 6: Activity
Introduction
An Activity represents a single screen with a user interface(UI). An Android App may have more
than one Activity, for example, An email App can have one activity to list all the emails, another
activity to show email contents, yet another activity to compose new email. All the activities in an
App work together to create perfect user experience.
Syntax
• void onCreate(Bundle savedInstanceState) // Called when the activity is starting.
• void onStart() // Called after onCreate(Bundle) — or after onRestart() when the activity had
been stopped, but is now again being displayed to the user.
• void onPostResume() // Called when activity resume is complete (after onResume() has
been called).
• void onRestart() // Called after onStop() when the current activity is being re-displayed to the
user (the user has navigated back to it).
• void onPause() // Called as part of the activity lifecycle when an activity is going into the
background, but has not (yet) been killed.
• void onStop() // Called when you are no longer visible to the user.
• void onNewIntent(Intent intent) // This is called for activities that set launchMode to
"singleTop" in their package, or if a client used the FLAG_ACTIVITY_SINGLE_TOP flag
when calling startActivity(Intent).
https://riptutorial.com/ 37
Parameters
Parameter Details
Remarks
An Activity is an application component that provides a screen with which users can interact in
order to do something, such as dial the phone, take a photo, send an email, or view a map. Each
activity is given a window in which to draw its user interface. The window typically fills the screen,
but may be smaller than the screen and float on top of other windows.
Examples
Exclude an activity from back-stack history
Let there be Activity B that can be opened, and can further start more Activities. But, user should
not encounter it when navigating back in task activities.
https://riptutorial.com/ 38
The simplest solution is to set the attribute noHistory to true for that <activity> tag in
AndroidManifest.xml:
<activity
android:name=".B"
android:noHistory="true">
This same behavior is also possible from code if B calls finish() before starting the next activity:
finish();
startActivity(new Intent(context, C.class));
Assume an application with a MainActivity which can call the Next Activity using a button click.
https://riptutorial.com/ 39
public class MainActivity extends AppCompatActivity {
@Override
protected void onPause() {
super.onPause();
Log.d(LOG_TAG, "calling onPause from MainActivity");
}
@Override
protected void onStop() {
super.onStop();
Log.d(LOG_TAG, "calling onStop from MainActivity");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(LOG_TAG, "calling onDestroy from MainActivity");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(LOG_TAG, "calling onRestart from MainActivity");
}
public void toNextActivity(){
Log.d(LOG_TAG, "calling Next Activity");
Intent intent = new Intent(this, NextActivity.class);
startActivity(intent);
} }
and
https://riptutorial.com/ 40
protected void onStart() {
super.onStart();
Log.d(LOG_TAG, "calling onStart from Next Activity");
}
@Override
protected void onResume() {
super.onResume();
Log.d(LOG_TAG, "calling onResume from Next Activity");
}
@Override
protected void onPause() {
super.onPause();
Log.d(LOG_TAG, "calling onPause from Next Activity");
}
@Override
protected void onStop() {
super.onStop();
Log.d(LOG_TAG, "calling onStop from Next Activity");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(LOG_TAG, "calling onDestroy from Next Activity");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(LOG_TAG, "calling onRestart from Next Activity");
} }
https://riptutorial.com/ 41
D/MainActivity: calling onStop from MainActivity
When Returning back to the Main Activity from Next Activity using back button
D/NextActivity: calling onPause from Next Activity
D/MainActivity: calling onRestart from MainActivity
D/MainActivity: calling onStart from MainActivity
D/MainActivity: calling onResume from MainActivity
D/NextActivity: calling onStop from Next Activity
D/NextActivity: calling onDestroy from Next Activity
Case2: When Activity is partially obscured (When overview button is pressed) or When app goes
to background and another app completely obscures it
D/MainActivity: calling onPause from MainActivity
D/MainActivity: calling onStop from MainActivity
and when the app is back in the foreground ready to accept User inputs,
D/MainActivity: calling onRestart from MainActivity
D/MainActivity: calling onStart from MainActivity
D/MainActivity: calling onResume from MainActivity
are called
Case3: When an activity is called to fulfill implicit intent and user has make a selection. For eg.,
when share button is pressed and user has to select an app from the list of applications shown
D/MainActivity: calling onPause from MainActivity
The activity is visible but not active now. When the selection is done and app is active
D/MainActivity: calling onResume from MainActivity
is called
Case4:
When the app is killed in the background(to free resources for another foreground app), onPause
(for pre-honeycomb device) or onStop(for since honeycomb device) will be the last to be called
before the app is terminated.
onCreate and onDestroy will be called utmost once each time the application is run. But the
onPause, onStop, onRestart, onStart, onResume maybe called many times during the lifecycle.
Activity launchMode
Launch mode defines the behaviour of new or existing activity in the task.
There are possible launch modes:
• standard
• singleTop
• singleTask
• singleInstance
https://riptutorial.com/ 42
<activity
android:launchMode=["standard" | "singleTop" | "singleTask" | "singleInstance"] />
Standard:
Default value. If this mode set, new activity will always be created for each new intent. So it's
possible to get many activities of same type. New activity will be placed on the top of the task.
There is some difference for different android version: if activity is starting from another
application, on androids <= 4.4 it will be placed on same task as starter application, but on >= 5.0
new task will be created.
SingleTop:
This mode is almost the same as standard. Many instances of singleTop activity could be created.
The difference is, if an instance of activity already exists on the top of the current stack,
onNewIntent() will be called instead of creating new instance.
SingleTask:
Activity with this launch mode can have only one instance in the system. New task for activity will
be created, if it doesn't exist. Otherwise, task with activity will be moved to front and onNewIntent
will be called.
SingleInstance:
This mode is similar to singleTask. The difference is task that holds an activity with singleInstance
could have only this activity and nothing more. When singleInstance activity create another
activity, new task will be created to place that activity.
Activity class takes care of creating a window for you in which you can place your UI with
setContentView.
There are three setContentView methods:
When setContentView is called, this view is placed directly into the activity's view hierarchy. It can
https://riptutorial.com/ 43
itself be a complex view hierarchy.
Examples
Set content from resource file:
Add resource file (main.xml in this example) with view hierarchy:
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello" />
</FrameLayout>
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
https://riptutorial.com/ 44
Clear your current Activity stack and launch a new Activity
If you want to clear your current Activity stack and launch a new Activity (for example, logging out
of the app and launching a log in Activity), there appears to be two approaches.
<activity
android:name="com.your_example_app.activities.ExitActivity"
android:autoRemoveFromRecents="true"
android:theme="@android:style/Theme.NoDisplay" />
/**
* Activity to exit Application without staying in the stack of last opened applications
*/
public class ExitActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Utils.hasLollipop()) {
finishAndRemoveTask();
} else if (Utils.hasJellyBean()) {
finishAffinity();
} else {
finish();
}
}
/**
* Exit Application and Exclude from Recents
*
* @param context Context to use
*/
public static void exitApplication(ApplicationContext context) {
Intent intent = new Intent(context, ExitActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(intent);
https://riptutorial.com/ 45
}
}
How is it done?
<application
android:name=".SkillSchoolApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".ui.activities.SplashActivity"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Now when i will click on the arrow inside the toolbar of HomeActivity it will take me back to the
parent activity.
Java Code
Here i will write the appropriate java code required for this functionality.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
ButterKnife.bind(this);
//Since i am using custom tool bar i am setting refernce of that toolbar to Actionbar.
If you are not using custom then you can simple leave this and move to next line
setSupportActionBar(toolbar);
getSupportActionBar.setDisplayHomeAsUpEnabled(true); // this will show the back arrow
in the tool bar.
}
https://riptutorial.com/ 46
}
If you run this code you will see when you press back button it will take you back to MainActivity.
For futher understanding of Up Navigation i would recommend reading docs
You can more customize this behaviour upon your needs by overriding
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this); // Here you will write your logic for handling
up navigation
return true;
}
return super.onOptionsItemSelected(item);
}
Simple Hack
This is simple hack which is mostly used to navigate to parent activity if parent is in backstack. By
calling onBackPressed() if id is equal to android.R.id.home
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case android.R.id.home:
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
https://riptutorial.com/ 47
Chapter 7: Activity Recognition
Introduction
Activity recognition is the detection of a user's physical activity in order to perform certain actions
on the device, such as taking points when a drive is detected, turn wifi off when a phone is still, or
putting the ring volume to max when the user is walking.
Examples
Google Play ActivityRecognitionAPI
Manifest
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
MainActivity.java
https://riptutorial.com/ 48
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
apiClient = new GoogleApiClient.Builder(this)
.addApi(ActivityRecognition.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
//This just gets the activity intent from the ActivityReceiver class
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localActivityReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
ActivityRecognitionResult recognitionResult =
ActivityRecognitionResult.extractResult(intent);
TextView textView = (TextView) findViewById(R.id.activityText);
//This is just to get the activity name. Use at your own risk.
textView.setText(DetectedActivity.zzkf(recognitionResult.getMostProbableActivity().getType()));
}
};
}
@Override
protected void onResume() {
super.onResume();
@Override
protected void onPause() {
super.onPause();
@Override
public void onConnected(@Nullable Bundle bundle) {
//Only register for activity recognition if google api client has connected
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(apiClient, 0,
PendingIntent.getBroadcast(this, 0, new Intent(this, ActivityReceiver.class),
PendingIntent.FLAG_UPDATE_CURRENT));
https://riptutorial.com/ 49
}
@Override
public void onConnectionSuspended(int i) {
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
}
}
ActivityReceiver
@Override
public void onReceive(Context context, Intent intent) {
LocalBroadcastManager.getInstance(context).sendBroadcast(intent.setAction("activity"));
}
}
PathSense activity recognition is another good library for devices which don't have Google Play
Services, as they have built their own activity recognition model, but requires developers register
at http://developer.pathsense.com to get an API key and Client ID.
Manifest
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<!-- You need to acquire these from their website (http://developer.pathsense.com) -->
<meta-data
android:name="com.pathsense.android.sdk.CLIENT_ID"
android:value="YOUR_CLIENT_ID" />
<meta-data
android:name="com.pathsense.android.sdk.API_KEY"
android:value="YOUR_API_KEY" />
</application>
https://riptutorial.com/ 50
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pathsenseLocationProviderApi = PathsenseLocationProviderApi.getInstance(this);
//This just gets the activity intent from the ActivityReceiver class
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localActivityReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//The detectedActivities object is passed as a serializable
PathsenseDetectedActivities detectedActivities = (PathsenseDetectedActivities)
intent.getSerializableExtra("ps");
TextView textView = (TextView) findViewById(R.id.activityText);
textView.setText(detectedActivities.getMostProbableActivity().getDetectedActivity().name());
}
};
}
@Override
protected void onResume() {
super.onResume();
//This gives an update everytime it receives one, even if it was the same as the last
update
pathsenseLocationProviderApi.requestActivityUpdates(ActivityReceiver.class);
// This gives updates only when it changes (ON_FOOT -> IN_VEHICLE for example)
// pathsenseLocationProviderApi.requestActivityChanges(ActivityReceiver.class);
}
@Override
protected void onPause() {
super.onPause();
pathsenseLocationProviderApi.removeActivityUpdates();
// pathsenseLocationProviderApi.removeActivityChanges();
ActivityReceiver.java
https://riptutorial.com/ 51
// You don't have to use their broadcastreceiver, but it's best to do so, and just pass the
result
// as needed to another class.
public class ActivityReceiver extends PathsenseActivityRecognitionReceiver {
@Override
protected void onDetectedActivities(Context context, PathsenseDetectedActivities
pathsenseDetectedActivities) {
Intent intent = new Intent("activity").putExtra("ps", pathsenseDetectedActivities);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
}
https://riptutorial.com/ 52
Chapter 8: ADB (Android Debug Bridge)
Introduction
ADB (Android Debug Bridge) is a command line tool that used to communicate with an emulator
instance or connected Android device.
Overview of ADB
Remarks
List of examples moved to adb shell:
Examples
Print verbose list of connected devices
To get a verbose list of all devices connected to adb, write the following command in your terminal:
adb devices -l
Example Output
https://riptutorial.com/ 53
• The first column is the serial number of the device. If it starts with emulator-, this device is an
emulator.
• usb: the path of the device in the USB subsystem.
• product: the product code of the device. This is very manufacturer-specific, and as you can
see in the case of the Archos device A50PL above, it can be blank.
• model: the device model. Like product, can be empty.
• device: the device code. This is also very manufacturer-specific, and can be empty.
This will print all available information in the form of key/value pairs.
You can just read specific information by appending the name of a specific key to the command.
For example:
Here are a few interesting pieces of information that you cat get:
[dalvik.vm.dex2oat-Xms]: [64m]
[dalvik.vm.dex2oat-Xmx]: [512m]
[dalvik.vm.heapsize]: [384m]
[dalvik.vm.image-dex2oat-Xms]: [64m]
[dalvik.vm.image-dex2oat-Xmx]: [64m]
[dalvik.vm.isa.x86.variant]: [dalvik.vm.isa.x86.features=default]
[dalvik.vm.isa.x86_64.features]: [default]
[dalvik.vm.isa.x86_64.variant]: [x86_64]
[dalvik.vm.lockprof.threshold]: [500]
[dalvik.vm.stack-trace-file]: [/data/anr/traces.txt]
[debug.atrace.tags.enableflags]: [0]
[debug.force_rtl]: [0]
[dev.bootcomplete]: [1]
[gsm.current.phone-type]: [1]
[gsm.defaultpdpcontext.active]: [true]
[gsm.network.type]: [UMTS]
[gsm.nitz.time]: [1469106902492]
[gsm.operator.alpha]: [Android]
[gsm.operator.iso-country]: [us]
[gsm.operator.isroaming]: [false]
[gsm.operator.numeric]: [310260]
[gsm.sim.operator.alpha]: [Android]
[gsm.sim.operator.iso-country]: [us]
https://riptutorial.com/ 54
[gsm.sim.operator.numeric]: [310260]
[gsm.sim.state]: [READY]
[gsm.version.ril-impl]: [android reference-ril 1.0]
[init.svc.adbd]: [running]
[init.svc.bootanim]: [stopped]
[init.svc.console]: [running]
[init.svc.debuggerd]: [running]
[init.svc.debuggerd64]: [running]
[init.svc.drm]: [running]
[init.svc.fingerprintd]: [running]
[init.svc.gatekeeperd]: [running]
[init.svc.goldfish-logcat]: [stopped]
[init.svc.goldfish-setup]: [stopped]
[init.svc.healthd]: [running]
[init.svc.installd]: [running]
[init.svc.keystore]: [running]
[init.svc.lmkd]: [running]
[init.svc.logd]: [running]
[init.svc.logd-reinit]: [stopped]
[init.svc.media]: [running]
[init.svc.netd]: [running]
[init.svc.perfprofd]: [running]
[init.svc.qemu-props]: [stopped]
[init.svc.ril-daemon]: [running]
[init.svc.servicemanager]: [running]
[init.svc.surfaceflinger]: [running]
[init.svc.ueventd]: [running]
[init.svc.vold]: [running]
[init.svc.zygote]: [running]
[init.svc.zygote_secondary]: [running]
[net.bt.name]: [Android]
[net.change]: [net.dns2]
[net.dns1]: [10.0.2.3]
[net.dns2]: [10.0.2.4]
[net.eth0.dns1]: [10.0.2.3]
[net.eth0.dns2]: [10.0.2.4]
[net.eth0.gw]: [10.0.2.2]
[net.gprs.local-ip]: [10.0.2.15]
[net.hostname]: [android-5e1af924d72dc578]
[net.qtaguid_enabled]: [1]
[net.tcp.default_init_rwnd]: [60]
[persist.sys.dalvik.vm.lib.2]: [libart.so]
[persist.sys.profiler_ms]: [0]
[persist.sys.timezone]: [Europe/Vienna]
[persist.sys.usb.config]: [adb]
[qemu.gles]: [1]
[qemu.hw.mainkeys]: [0]
[qemu.sf.fake_camera]: [none]
[qemu.sf.lcd_density]: [560]
[rild.libargs]: [-d /dev/ttyS0]
[rild.libpath]: [/system/lib/libreference-ril.so]
[ro.allow.mock.location]: [0]
[ro.baseband]: [unknown]
[ro.board.platform]: []
[ro.boot.hardware]: [ranchu]
[ro.bootimage.build.date]: [Thu Jul 7 15:56:30 UTC 2016]
[ro.bootimage.build.date.utc]: [1467906990]
[ro.bootimage.build.fingerprint]:
[Android/sdk_google_phone_x86_64/generic_x86_64:6.0/MASTER/3038907:userdebug/test-keys]
[ro.bootloader]: [unknown]
[ro.bootmode]: [unknown]
https://riptutorial.com/ 55
[ro.build.characteristics]: [emulator]
[ro.build.date]: [Thu Jul 7 15:55:30 UTC 2016]
[ro.build.date.utc]: [1467906930]
[ro.build.description]: [sdk_google_phone_x86_64-userdebug 6.0 MASTER 3038907 test-keys]
[ro.build.display.id]: [sdk_google_phone_x86_64-userdebug 6.0 MASTER 3038907 test-keys]
[ro.build.fingerprint]:
[Android/sdk_google_phone_x86_64/generic_x86_64:6.0/MASTER/3038907:userdebug/test-keys]
[ro.build.flavor]: [sdk_google_phone_x86_64-userdebug]
[ro.build.host]: [vpak15.mtv.corp.google.com]
[ro.build.id]: [MASTER]
[ro.build.product]: [generic_x86_64]
[ro.build.tags]: [test-keys]
[ro.build.type]: [userdebug]
[ro.build.user]: [android-build]
[ro.build.version.all_codenames]: [REL]
[ro.build.version.base_os]: []
[ro.build.version.codename]: [REL]
[ro.build.version.incremental]: [3038907]
[ro.build.version.preview_sdk]: [0]
[ro.build.version.release]: [6.0]
[ro.build.version.sdk]: [23]
[ro.build.version.security_patch]: [2015-10-01]
[ro.com.google.locationfeatures]: [1]
[ro.config.alarm_alert]: [Alarm_Classic.ogg]
[ro.config.nocheckin]: [yes]
[ro.config.notification_sound]: [OnTheHunt.ogg]
[ro.crypto.state]: [unencrypted]
[ro.dalvik.vm.native.bridge]: [0]
[ro.debuggable]: [1]
[ro.hardware]: [ranchu]
[ro.hardware.audio.primary]: [goldfish]
[ro.kernel.android.checkjni]: [1]
[ro.kernel.android.qemud]: [1]
[ro.kernel.androidboot.hardware]: [ranchu]
[ro.kernel.clocksource]: [pit]
[ro.kernel.console]: [0]
[ro.kernel.ndns]: [2]
[ro.kernel.qemu]: [1]
[ro.kernel.qemu.gles]: [1]
[ro.opengles.version]: [131072]
[ro.product.board]: []
[ro.product.brand]: [Android]
[ro.product.cpu.abi]: [x86_64]
[ro.product.cpu.abilist]: [x86_64,x86]
[ro.product.cpu.abilist32]: [x86]
[ro.product.cpu.abilist64]: [x86_64]
[ro.product.device]: [generic_x86_64]
[ro.product.locale]: [en-US]
[ro.product.manufacturer]: [unknown]
[ro.product.model]: [Android SDK built for x86_64]
[ro.product.name]: [sdk_google_phone_x86_64]
[ro.radio.use-ppp]: [no]
[ro.revision]: [0]
[ro.runtime.firstboot]: [1469106908722]
[ro.secure]: [1]
[ro.serialno]: []
[ro.wifi.channels]: []
[ro.zygote]: [zygote64_32]
[selinux.reload_policy]: [1]
[service.bootanim.exit]: [1]
[status.battery.level]: [5]
https://riptutorial.com/ 56
[status.battery.level_raw]: [50]
[status.battery.level_scale]: [9]
[status.battery.state]: [Slow]
[sys.boot_completed]: [1]
[sys.sysctl.extra_free_kbytes]: [43200]
[sys.sysctl.tcp_def_init_rwnd]: [60]
[sys.usb.config]: [adb]
[sys.usb.state]: [adb]
[vold.has_adoptable]: [1]
[wlan.driver.status]: [unloaded]
[xmpp.auto-presence]: [true]
• Make sure your device and your computer are on the same network.
While your device is connected to adb via USB, do the following command to listen for a
TCP/IP connection on a port (default 5555):
For example:
adb usb
https://riptutorial.com/ 57
You can also connect ADB via WiFi by installing a plugin to Android Studio. In order to do so,
go to Settings > Plugins and Browse repositories, search for ADB WiFi, install it, and reopen
Android Studio. You will see a new icon in your toolbar as shown in the following image.
Connect the device to the host computer via USB and click on this AndroidWiFiADB icon. It
will display a message whether your device is connected or not. Once it gets connected you
can unplug your USB.
Rooted device
Note: Some devices which are rooted can use the ADB WiFi App from the Play Store to enable
this in a simple way. Also, for certain devices (especially those with CyanogenMod ROMs) this
option is present in the Developer Options among the Settings. Enabling it will give you the IP
address and port number required to connect to adb by simply executing adb connect <ip
address>:<port>.
When you have a rooted device but don't have access to a USB cable
su
setprop service.adb.tcp.port <a tcp port number>
stop adbd
start adbd
For example:
For example:
To turn it off:
setprop service.adb.tcp.port -1
https://riptutorial.com/ 58
stop adbd
start adbd
Avoid timeout
By default adb will timeout after 5000 ms. This can happen in some cases such as slow WiFi or
large APK.
android {
adbOptions {
timeOutInMs 10 * 1000
}
}
You may pull (download) files from the device by executing the following command:
For example:
You may also push (upload) files from your computer to the device:
For example:
Reboot device
adb reboot
https://riptutorial.com/ 59
adb reboot bootloader
Turn on:
Turn off:
Command:
adb devices
Result example:
Android documentation
Connect device by IP
su
setprop service.adb.tcp.port 5555
stop adbd
start adbd
After this, you can use CMD and ADB to connect using the following command
https://riptutorial.com/ 60
adb connect 192.168.0.101.5555
And you can disable it and return ADB to listening on USB with
setprop service.adb.tcp.port -1
stop adbd
start adbd
From a computer, if you have USB access already (no root required)
It is even easier to switch to using Wi-Fi, if you already have USB. From a command line on the
computer that has the device connected via USB, issue the commands
Start/stop adb
Start ADB:
adb kill-server
Stop ADB:
adb start-server
View logcat
You can run logcat as an adb command or directly in a shell prompt of your emulator or connected
device. To view log output using adb, navigate to your SDK platform-tools/ directory and execute:
$ adb logcat
Alternatively, you can create a shell connection to a device and then execute:
$ adb shell
$ logcat
This displays the date, invocation time, priority, tag, and the PID and TID of the thread issuing the
message in a long message format.
https://riptutorial.com/ 61
Filtering
You can filter logcat by log level as well. For instance if you want only to output Debug level:
Logcat can be filtered by a package name, of course you can combine it with the log level filter:
You can also filter the log using grep (more on filtering logcat output here):
To view alternative log buffer [main|events|radio], run the logcat with the -b option:
adb logcat -c
Use the -s option followed by a device name to select on which device the adb command should
run. The -s options should be first in line, before the command.
https://riptutorial.com/ 62
Example:
adb devices
Example#2:
adb devices -l
adb -e <command>
adb -d <command>
Taking a screenshot and video (for kitkat only) from a device display
You can then use the pull command to download the file from the device into the current directory
on you computer:
The -p flag redirects the output of the screencap command to stdout. The Perl expression this is
piped into cleans up some end-of-line issues on Marshmallow and earlier. The stream is then
written to a file named screen.png within the current directory. See this article and this article for
more information.
Video
this only work in KitKat and via ADB only. This not Working below Kitkat To start recording your
device’s screen, run the following command:
adb shell screenrecord /sdcard/example.mp4, This command will start recording your device’s
screen using the default settings and save the resulting video to a file at /sdcard/example.mp4 file on
your device.
When you’re done recording, press Ctrl+C (z in Linux) in the Command Prompt window to stop the
screen recording. You can then find the screen recording file at the location you specified. Note
that the screen recording is saved to your device’s internal storage, not to your computer.
The default settings are to use your device’s standard screen resolution, encode the video at a
bitrate of 4Mbps, and set the maximum screen recording time to 180 seconds. For more
information about the command-line options you can use, run the following command:
adb shell screenrecord –help, This works without rooting the device. Hope this helps.
One can clear the user data of a specific app using adb:
This is the same as to browse the settings on the phone, select the app and press on the clear
data button.
Sending broadcast
https://riptutorial.com/ 64
It's possible to send broadcast to BroadcastReceiver with adb.
In this example we are sending broadcast with action com.test.app.ACTION and string extra in
bundle 'foo'='bar':
You can put any other supported type to bundle, not only strings:
--ez - boolean
--ei - integer
--el - long
--ef - float
--eu - uri
--eia - int array (separated by ',')
--ela - long array (separated by ',')
--efa - float array (separated by ',')
--esa - string array (separated by ',')
-p com.test.app
-n com.test.app/.SomeReceiver
Useful examples:
https://riptutorial.com/ 65
Use the following command to start an app with a provided package name (or a specific activity in
an app):
Backup
You can use the adb backup command to backup your device.
How to install the Android Debugging Bridge (ADB) to a Linux system with the terminal using your
distro's repositories.
https://riptutorial.com/ 66
sudo apt-get update
sudo apt-get install adb
List all permissions that require runtime grant from users on Android 6.0
First, make sure your app can be backed up in AndroidManifest.xml, i.e. android:allowBackup is not
false.
Backup command:
https://riptutorial.com/ 67
adb -s <serialNumber> shell dumpsys activity activities
Very useful when used together with the watch unix command:
watch -n 5 "adb -s <serialNumber> shell dumpsys activity activities | sed -En -e '/Stack #/p'
-e '/Running activities/,/Run #0/p'"
You may use this command for listing the files for your own debuggable apk:
And this script for pulling from cache, this copy the content to sdcard first, pull and then remove it
at the end:
#!/bin/sh
adb shell "run-as <sample.package.id> cat '/data/data/<sample.package.id>/$1' > '/sdcard/$1'"
adb pull "/sdcard/$1"
adb shell "rm '/sdcard/$1'"
./pull.sh cache/someCachedData.txt
https://riptutorial.com/ 68
Chapter 9: adb shell
Introduction
adb shellopens a Linux shell in a target device or emulator. It is the most powerful and versatile
way to control an Android device via adb.
This topic was split from ADB (Android Debug Bridge) due to reaching the limit of examples, many
of which were involving adb shell command.
Syntax
• adb shell [-e escape] [-n] [-Tt] [-x] [command]
Parameters
Parameter Details
Examples
Send text, key pressed and touch events to Android Device via ADB
execute the following command to insert the text into a view with a focus (if it supports text input)
6.0
6.0
https://riptutorial.com/ 69
Send text prior to SDK 23
Spaces are not accepted as the input, replace them with %s.
Send events
or alternatively
Even if you don't have a hardware key you still can use a keyevent to perform the equivalent action
To run a script in Ubuntu, Create script.sh right click the file and add read/write
permission and tick allow executing file as program.
Script.sh
https://riptutorial.com/ 70
• shortlist of several interesting events ADB Shell Input Events
• reference documentation
https://developer.android.com/reference/android/view/KeyEvent.html#KEYCODE_POWER.
List packages
Prints all packages, optionally only those whose package name contains the text in <FILTER>.
All <FILTER>
Attributes:
• granting
• revoking
https://riptutorial.com/ 71
adb install -g /path/to/sample_package.apk
• version code
• version name
• granted permissions (Android API 23+)
• etc..
4.4
Recording the display of devices running Android 4.4 (API level 19) and higher:
Note: Stop the screen recording by pressing Ctrl-C, otherwise the recording stops
automatically at three minutes or the time limit set by --time-limit.
Sets the video size: 1280x720. The default value is the device's native display resolution (if
supported), 1280x720 if not. For best results, use a size supported by your device's Advanced
Video Coding (AVC) encoder.
Sets the video bit rate for the video, in megabits per second. The default value is 4Mbps. You can
increase the bit rate to improve video quality, but doing so results in larger movie files. The
following example sets the recording bit rate to 5Mbps:
https://riptutorial.com/ 72
adb shell screenrecord --time-limit <TIME>
Sets the maximum recording time, in seconds. The default and maximum value is 180 (3 minutes).
Displays log information on the command-line screen. If you do not set this option, the utility does
not display any information while running.
4.4
The screen recording command isn't compatible with android versions pre 4.4
The screenrecord command is a shell utility for recording the display of devices running
Android 4.4 (API level 19) and higher. The utility records screen activity to an MPEG-4
file.
Notice, that in order to change file prmissions, your device need to be rooted, su binary
doesn't come with factory shipped devices!
Convention:
For example, if you want to change file to be readable, writable and executable by everyone, this
will be your command:
Or
1st digit-specifies user permission, 2nd digit- specifies group permission, 3rd digit - specifies
world (others) permission.
https://riptutorial.com/ 73
Access permissions:
6.0
For example, to set July 17'th 10:10am, without changing the current year, type:
Tip 1: the date change will not be reflected immediately, and a noticable change will happen only
after the system clock advances to the next minute.
You can force an update by attaching a TIME_SET intent broadcast to your call, like that:
Linux:
Windows (PowerShell):
6.0
Tip: to synchronize Android's clock with your local (linux based) machine:
https://riptutorial.com/ 74
adb shell date -s `date +%G%m%d.%H%M%S`
This is relevant for apps that implement a BootListener. Test your app by killing your app and then
test with:
View content:
View path:
Sometimes Android's logcat is running infinitely with errors coming from some process not own by
you, draining battery or just making it hard to debug your code.
A convenient way to fix the problem without restarting the device is to locate and kill the process
causing the problem.
From Logcat
Now we can open a shell and kill the process. Note that we cannot kill root process.
adb shell
https://riptutorial.com/ 75
inside the shell we can check more about the process using
ps -x | grep 1550
kill -9 1550
https://riptutorial.com/ 76
Chapter 10: Adding a FuseView to an Android
Project
Introduction
Export a Fuse.View from fusetools and use it inside an existing android project.
Our goal is to export the entire hikr sample app and use it inside an Activity.
Examples
hikr app, just another android.view.View
Prerequisites
Step 1
Find hikr.unoproj file inside the project root folder and add "Fuse.Views" to the "Packages" array.
{
"RootNamespace":"",
"Packages": [
"Fuse",
"FuseJS",
"Fuse.Views"
],
"Includes": [
"*",
"Modules/*.js:Bundle"
]
}
https://riptutorial.com/ 77
3.1 In the project root folder make a new file called HikrApp.ux and paste the contents of
MainView.ux.
HikrApp.ux
<App Background="#022328">
<iOS.StatusBarConfig Style="Light" />
<Android.StatusBarConfig Color="#022328" />
<ClientPanel>
<Navigator DefaultPath="splash">
<SplashPage ux:Template="splash" router="router" />
<HomePage ux:Template="home" router="router" />
<EditHikePage ux:Template="editHike" router="router" />
</Navigator>
</ClientPanel>
</App>
3.2 In HikrApp.ux
HikrApp.ux
<Navigator DefaultPath="splash">
<SplashPage ux:Template="splash" router="router" />
<HomePage ux:Template="home" router="router" />
<EditHikePage ux:Template="editHike" router="router" />
</Navigator>
</Page>
<App>
<HikrApp/>
</App>
Our app is back to its normal behavior, but we now have extracted it to a separate component
called HikrApp
https://riptutorial.com/ 78
Step 4 Inside MainView.ux replace the <App> tags with <ExportedViews> and add
ux:Template="HikrAppView" to <HikrApp />
<ExportedViews>
<HikrApp ux:Template="HikrAppView" />
</ExportedViews>
Remember the template HikrAppView, because we'll need it to get a reference to our view from
Java.
ExportedViews will behave as App when doing normal fuse preview and uno build
Not true. You will get this error when previewing from Fuse Studio:
Error: Couldn't find an App tag in any of the included UX files. Have you forgot to
include the UX file that contains the app tag?
<Page ux:Class="SplashPage">
<Router ux:Dependency="router" />
<GraphicsView>
<DockPanel ClipToBounds="true">
<Video Layer="Background" File="../Assets/nature.mp4" IsLooping="true"
AutoPlay="true" StretchMode="UniformToFill" Opacity="0.5">
<Blur Radius="4.75" />
</Video>
<Grid RowCount="2">
<StackPanel Alignment="VerticalCenter">
<hikr.Text Alignment="HorizontalCenter" FontSize="70">hikr</hikr.Text>
<hikr.Text Alignment="HorizontalCenter" Opacity=".5">get out
there</hikr.Text>
</StackPanel>
https://riptutorial.com/ 79
Step 6 Export the fuse project as an aar library
// Top-level build file where you can add configuration options common to all sub-
projects/modules.
buildscript { ... }
...
allprojects {
repositories {
jcenter()
flatDir {
dirs 'libs'
}
}
}
...
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "com.shiftstudio.fuseviewtest"
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile(name: 'app-debug', ext: 'aar')
https://riptutorial.com/ 80
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
testCompile 'junit:junit:4.12'
}
android:launchMode="singleTask"
android:taskAffinity=""
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize"
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:taskAffinity=""
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</manifest>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
https://riptutorial.com/ 81
final FrameLayout root = (FrameLayout) findViewById(R.id.fuse_root);
final View fuseApp = fuseHandle.getView();
root.addView(fuseApp);
}
}
activity_main.xml
<TextView
android:layout_width="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="24sp"
android:textStyle="bold"
android:layout_height="wrap_content"
android:text="Hello World, from Kotlin" />
<FrameLayout
android:id="@+id/fuse_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:text="THIS IS FROM NATIVE.\nBEHIND FUSE VIEW"
android:layout_gravity="center"
android:textStyle="bold"
android:textSize="30sp"
android:background="@color/colorAccent"
android:textAlignment="center"
android:layout_height="wrap_content" />
</FrameLayout>
</LinearLayout>
Note
When you press the back button, on android, the app crashes. You can follow the issue on the
fuse forum.
A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0xdeadcab1 in tid 18026
(io.fuseviewtest)
https://riptutorial.com/ 82
[ 05-25 11:52:33.658 16567:16567 W/ ]
And the final result is something like this. You can also find a short clip on github.
https://riptutorial.com/ 83
https://riptutorial.com/ 84
https://riptutorial.com/android/topic/10052/adding-a-fuseview-to-an-android-project
https://riptutorial.com/ 85
Chapter 11: AdMob
Syntax
• compile 'com.google.firebase:firebase-ads:10.2.1' //NOTE: SET TO NEWEST VERSION IF
AVAILABLE
• <uses-permission android:name="android.permission.INTERNET" /> Required to retrieve the ad
• AdRequest adRequest = new AdRequest.Builder().build();//Banner ad
• AdView mAdView = (AdView) findViewById(R.id.adView);//Banner ad
• mAdView.loadAd(adRequest);//Banner ad
Parameters
Param Details
Remarks
• Requires a valid Admob account
• Read the admob policy. Make sure you do not do anything that can get your admob account
suspended
Examples
Implementing
Note: This example requires a valid Admob account and valid Admob ad code.
compile 'com.google.firebase:firebase-ads:10.2.1'
https://riptutorial.com/ 86
Manifest
Internet permission is required to access the ad data. Note that this permission does not have to
be requested (using API 23+) as it is a normal permission and not dangerous:
XML
The following XML example shows a banner ad:
<com.google.android.gms.ads.AdView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/adView"
ads:adSize="BANNER"
ads:adUnitId="@string/main_screen_ad" />
For the code of other types, refer to the Google AdMob Help.
Java
The following code is for the integration of banner ads. Note that other ad types may require
different integration:
Add the AdView life cycle methods in the onResume(), onPause(), and onDestroy() methods of your
activity:
@Override
public void onPause() {
if (mAdView != null) {
mAdView.pause();
}
super.onPause();
}
@Override
public void onResume() {
super.onResume();
https://riptutorial.com/ 87
if (mAdView != null) {
mAdView.resume();
}
}
@Override
public void onDestroy() {
if (mAdView != null) {
mAdView.destroy();
}
super.onDestroy();
}
https://riptutorial.com/ 88
Chapter 12: AIDL
Introduction
AIDL is Android interface definition language.
What? It is a bounded services. This AIDL service will be active till atleast one of the client is exist.
It works based on marshaling and unmarshaling concept.
Why? Remote applications can access your service + Multi Threading.(Remote application
request).
How? Create the .aidl file Implement the interface Expose the interface to clients
Examples
AIDL Service
ICalculator.aidl
interface ICalculator {
int add(int x,int y);
int sub(int x,int y);
}
AidlService.java
public AidlService() {
Log.i(TAG, className+" Constructor");
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
Log.i(TAG, className+" onBind");
return iCalculator.asBinder();
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, className+" onCreate");
https://riptutorial.com/ 89
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, className+" onDestroy");
}
@Override
public int sub(int x, int y) throws RemoteException {
Log.i(TAG, className+" add Thread Name: "+Thread.currentThread().getName());
int z = x-y;
return z;
}
};
Service Connection
@Override
public void onServiceDisconnected(ComponentName name) {
unbindService(serviceConnection);
}
};
https://riptutorial.com/ 90
Chapter 13: AlarmManager
Examples
Run an intent at a later time
1. Create a receiver. This class will receive the intent and handle it how you wish.
2. Give an intent to AlarmManager. This example will trigger the intent to be sent to
AlarmReceiver after 1 minute.
If you want to cancel an alarm, and you don't have a reference to the original PendingIntent used
to set the alarm, you need to recreate a PendingIntent exactly as it was when it was originally
created.
if their action, data, type, class, and categories are the same. This does not compare
any extra data included in the intents.
https://riptutorial.com/ 91
PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, targetTimeInMillis, pendingIntent);
Here is how you would create a new PendingIntent reference that you can use to cancel the alarm
with a new AlarmManager reference:
With more and more battery optimizations being put into the Android system over time, the
methods of the AlarmManager have also significantly changed (to allow for more lenient timing).
However, for some applications it is still required to be as exact as possible on all Android
versions. The following helper uses the most accurate method available on all platforms to
schedule a PendingIntent:
Android 6 (API23) introduced Doze mode which interferes with AlarmManager. It uses certain
maintenance windows to handle alarms, so even if you used setExactAndAllowWhileIdle() you
cannot make sure that your alarm fires at the desired point of time.
You can turn this behavior off for your app using your phone's settings (Settings/General/Battery &
power saving/Battery usage/Ignore optimizations or similar)
https://riptutorial.com/ 92
... and eventually show the respective settings dialog:
https://riptutorial.com/ 93
Chapter 14: Android Architecture
Components
Introduction
Android Architecture Components is new collection of libraries that help you design robust,
testable, and maintainable apps. Main parts are: Lifecycles, ViewModel, LiveData, Room.
Examples
Add Architecture Components
Project build.gradle
allprojects {
repositories {
jcenter()
// Add this if you use Gradle 4.0+
google()
// Add this if you use Gradle < 4.0
maven { url 'https://maven.google.com' }
}
}
ext {
archVersion = '1.0.0-alpha5'
}
// For Room
compile "android.arch.persistence.room:runtime:$archVersion"
annotationProcessor "android.arch.persistence.room:compiler:$archVersion"
https://riptutorial.com/ 94
public abstract class BaseCompatLifecycleActivity extends AppCompatActivity implements
LifecycleRegistryOwner {
// We need this class, because LifecycleActivity extends FragmentActivity not
AppCompatActivity
@NonNull
private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
@NonNull
@Override
public LifecycleRegistry getLifecycle() {
return lifecycleRegistry;
}
}
ContentRepository contentRepository;
public BaseViewModel() {
// some inits
}
Somewhere in UI:
https://riptutorial.com/ 95
public class VideoActivity extends BaseCompatLifecycleActivity {
private VideoViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get ViewModel
viewModel = ViewModelProviders.of(this).get(BaseViewModel.class);
// Add observer
viewModel.getVideoByTagData().observe(this, data -> {
// some checks
adapter.updateData(data);
});
...
if (savedInstanceState == null) {
// init loading only at first creation
// you just set params and
viewModel.setUrlWithReferrer(url, referrer);
}
}
Room peristence
Room require four parts: Database class, DAO classes, Entity classes and Migration classes (now
you may use only DDL methods):
Entity classes
https://riptutorial.com/ 96
DAO classes
@Dao
public interface VideoDao {
// Create insert with custom conflict strategy
@Insert(onConflict = OnConflictStrategy.REPLACE)
void saveVideos(List<VideoItem> videos);
// Simple update
@Update
void updateVideos(VideoItem... videos);
Database class
Migrations
private Migrations() {
}
}
https://riptutorial.com/ 97
ContentDatabase provideContentDatabase() {
return Room.databaseBuilder(context, ContentDatabase.class, "data.db")
.addMigrations(Migrations.ALL).build();
}
Use in ViewModel:
Custom LiveData
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// Do something
}
@Override
public void onProviderEnabled(String provider) {
// Do something
}
@Override
public void onProviderDisabled(String provider) {
// Do something
}
https://riptutorial.com/ 98
};
@Override
protected void onActive() {
// We have observers, start working
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
}
@Override
protected void onInactive() {
// We have no observers, stop working
locationManager.removeUpdates(listener);
}
}
You may create component, that will be notified on lifecycle state change:
@OnLifecycleEvent(Lifecycle.Event.ON_START)
void start() {
https://riptutorial.com/ 99
if (enabled) {
// connect
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop() {
// disconnect if connected
}
}
https://riptutorial.com/ 100
Chapter 15: Android Authenticator
Examples
Basic Account Authenticator Service
The Android Account Authenticator system can be used to make the client authenticate with a
remote server. Three pieces of information are required:
1. The service:
<service android:name="com.example.MyAuthenticationService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
Note that the android.accounts.AccountAuthenticator is included within the intent-filter tag. The
xml resource (named authenticator here) is specified in the meta-data tag.
public MyAuthenticationService() {
super();
}
@Override
public void onCreate() {
super.onCreate();
https://riptutorial.com/ 101
synchronized (lock) {
if (mAuthenticator == null) {
mAuthenticator = new MyAuthenticator(this);
}
}
}
@Override
public IBinder onBind(Intent intent) {
return mAuthenticator.getIBinder();
}
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.example.account"
android:icon="@drawable/appicon"
android:smallIcon="@drawable/appicon"
android:label="@string/app_name" />
Do not directly assign a string to android:label or assign missing drawables. It will crash without
warning.
@Override
public Bundle addAccount(AccountAuthenticatorResponse response,
String accountType,
String authTokenType,
String[] requiredFeatures,
Bundle options) throws NetworkErrorException {
return bundle;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account,
Bundle options) throws NetworkErrorException {
return null;
}
https://riptutorial.com/ 102
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String
authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public String getAuthTokenLabel(String authTokenType) {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[]
features) throws NetworkErrorException {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
}
https://riptutorial.com/ 103
Chapter 16: Android game development
Introduction
A short introduction to creating a game on the Android platform using Java
Remarks
• The first example covers the basics: There are no objectives, but it shows you how you
create a basic part of a 2D game using SurfaceView.
• Make sure to save any important data when you create a game; everything else will be lost
Examples
Game using Canvas and SurfaceView
This covers how you can create a basic 2D game using SurfaceView.
Now for the game itself. First, we start by implementing a game thread:
/**
* Holds the surface frame
*/
private SurfaceHolder holder;
/**
* Draw thread
*/
https://riptutorial.com/ 104
private Thread drawThread;
/**
* True when the surface is ready to draw
*/
private boolean surfaceReady = false;
/**
* Drawing thread flag
*/
/**
* Time per frame for 60 FPS
*/
private static final int MAX_FRAME_TIME = (int) (1000.0 / 60.0);
/*
* All the constructors are overridden to ensure functionality if one of the different
constructors are used through an XML file or programmatically
*/
public Game(Context context) {
super(context);
init();
}
public Game(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public Game(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@TargetApi(21)
public Game(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
@Override
https://riptutorial.com/ 105
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
if (width == 0 || height == 0){
return;
}
// resize your UI
}
@Override
public void surfaceCreated(SurfaceHolder holder){
this.holder = holder;
if (drawThread != null){
Log.d(LOGTAG, "draw thread still active..");
drawingActive = false;
try{
drawThread.join();
} catch (InterruptedException e){}
}
surfaceReady = true;
startDrawThread();
Log.d(LOGTAG, "Created");
}
@Override
public void surfaceDestroyed(SurfaceHolder holder){
// Surface is not used anymore - stop the drawing thread
stopDrawThread();
// and release the surface
holder.getSurface().release();
this.holder = null;
surfaceReady = false;
Log.d(LOGTAG, "Destroyed");
}
@Override
public boolean onTouchEvent(MotionEvent event){
// Handle touch events
return true;
}
/**
* Stops the drawing thread
*/
public void stopDrawThread(){
if (drawThread == null){
Log.d(LOGTAG, "DrawThread is null");
return;
}
drawingActive = false;
while (true){
try{
Log.d(LOGTAG, "Request last frame");
drawThread.join(5000);
break;
} catch (Exception e) {
Log.e(LOGTAG, "Could not join with draw thread");
}
https://riptutorial.com/ 106
}
drawThread = null;
}
/**
* Creates a new draw thread and starts it.
*/
public void startDrawThread(){
if (surfaceReady && drawThread == null){
drawThread = new Thread(this, "Draw thread");
drawingActive = true;
drawThread.start();
}
}
@Override
public void run() {
Log.d(LOGTAG, "Draw thread started");
long frameStartTime;
long frameTime;
/*
* In order to work reliable on Nexus 7, we place ~500ms delay at the start of drawing
thread
* (AOSP - Issue 58385)
*/
if (android.os.Build.BRAND.equalsIgnoreCase("google") &&
android.os.Build.MANUFACTURER.equalsIgnoreCase("asus") &&
android.os.Build.MODEL.equalsIgnoreCase("Nexus 7")) {
Log.w(LOGTAG, "Sleep 500ms (Device: Asus Nexus 7)");
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {}
}
while (drawing) {
if (sf == null) {
return;
}
frameStartTime = System.nanoTime();
Canvas canvas = sf.lockCanvas();
if (canvas != null) {
try {
synchronized (sf) {
tick();
render(canvas);
}
} finally {
sf.unlockCanvasAndPost(canvas);
}
}
https://riptutorial.com/ 107
// ignore
}
}
}
Log.d(LOGTAG, "Draw thread finished");
}
}
That is the basic part. Now you have the ability to draw onto the screen.
public final int x = 100;//The reason for this being static will be shown when the game is
runnable
public int y;
public int velY;
For this next part, you are going to need an image. It should be about 100x100 but it can be bigger
or smaller. For learning, a Rect can also be used(but that requires change in code a little bit down)
...
c.drawBitmap(PLAYER_BMP, x, y, null);
...
boolean up = false;
in onTouchEvent, we add:
if(ev.getAction() == MotionEvent.ACTION_DOWN){
up = true;
}else if(ev.getAction() == MotionEvent.ACTION_UP){
up = false;
}
if(up){
velY -=1;
}
else{
https://riptutorial.com/ 108
velY +=1;
}
if(velY >14)velY = 14;
if(velY <-14)velY = -14;
y += velY *2;
At this point, the game is runnable. Meaning you can launch it and test it.
Now you should have a player image or rect going up and down the screen. The player can be
created as a custom class if needed. Then all the player-related things can be moved into that
class, and use an instance of that class to move, render and do other logic.
Now, as you probably saw under testing it flies off the screen. So we need to limit it.
In init, after initializing width and height, we create a new rect that is the screen.
and in tick:
if(!getPlayerBound().intersects(screen){
gameOver = true;
}
The implementation of gameOVer can also be used to show the start of a game.
https://riptutorial.com/ 109
Other aspects of a game worth noting:
https://riptutorial.com/ 110
Chapter 17: Android Java Native Interface
(JNI)
Introduction
JNI (Java Native Interface) is a powerful tool that enables Android developers to utilize the NDK
and use C++ native code in their applications. This topic describes the usage of Java <-> C++
interface.
Examples
How to call functions in a native library via the JNI interface
The Java Native Interface (JNI) allows you to call native functions from Java code, and vice versa.
This example shows how to load and call a native function via JNI, it does not go into accessing
Java methods and fields from native code using JNI functions.
#include <jni.h>
The pEnv argument is a pointer to the JNI environment that you can pass to JNI functions to
access methods and fields of Java objects and classes. The thiz pointer is a jobject reference to
the Java object that the native method was called on (or the class if it is a static method).
static{
System.loadLibrary("jniexample");
}
Note the lib at the start, and the .so at the end of the filename are omitted.
https://riptutorial.com/ 111
Call the native function from Java like this:
The Java Native Interface (JNI) allows you to call Java functions from native code. Here is a
simple example of how to do it:
Java code:
package com.example.jniexample;
public class JNITest {
public static int getAnswer(bool) {
return 42;
}
}
Native code:
int getTheAnswer()
{
// Get JNI environment
JNIEnv *env = JniGetEnv();
// Find the Java class - provide package ('.' replaced to '/') and class name
jclass jniTestClass = env->FindClass("com/example/jniexample/JNITest");
// Find the Java method - provide parameters inside () and return value (see table below
for an explanation of how to encode them)
jmethodID getAnswerMethod = env->GetStaticMethodID(jniTestClass, "getAnswer", "(Z)I;");
Z boolean
B byte
C char
S short
I int
J long
https://riptutorial.com/ 112
JNI Signature Java Type
F float
D double
L fully-qualified-class ; fully-qualified-class
[ type type[]
So for our example we used (Z)I - which means the function gets a boolean and returns an int.
This method will help to get the Java string from C++ string.
if (!cstring) {
return nullString;
}
global_env->DeleteLocalRef(strClass);
global_env->DeleteLocalRef(encoding);
global_env->DeleteLocalRef(bytes);
return str;
}
https://riptutorial.com/ 113
Chapter 18: Android Kernel Optimization
Examples
Low RAM Configuration
Android now supports devices with 512MB of RAM. This documentation is intended to help OEMs
optimize and configure Android 4.4 for low-memory devices. Several of these optimizations are
generic enough that they can be applied to previous releases as well.
For 512MB devices, this API is expected to return: "true" It can be enabled by the following system
property in the device makefile.
PRODUCT_PROPERTY_OVERRIDES += ro.config.low_ram=true
Disable JIT
System-wide JIT memory usage is dependent on the number of applications running and the code
footprint of those applications. The JIT establishes a maximum translated code cache size and
touches the pages within it as needed. JIT costs somewhere between 3M and 6M across a typical
running system.
The large apps tend to max out the code cache fairly quickly (which by default has been 1M). On
average, JIT cache usage runs somewhere between 100K and 200K bytes per app. Reducing the
max size of the cache can help somewhat with memory usage, but if set too low will send the JIT
into a thrashing mode. For the really low-memory devices, we recommend the JIT be disabled
entirely.
This can be achieved by adding the following line to the product makefile:
PRODUCT_PROPERTY_OVERRIDES += dalvik.vm.jit.codecachesize=0
The CPU governor itself is just 1 C file, which is located in kernel_source/drivers/cpufreq/, for
example: cpufreq_smartass2.c. You are responsible yourself for find the governor (look in an
existing kernel repo for your device) But in order to successfully call and compile this file into your
kernel you will have to make the following changes:
https://riptutorial.com/ 114
now paste it.
2. and open Kconfig (this is the interface of the config menu layout) when adding a kernel, you
want it to show up in your config. You can do that by adding the choice of governor.
config CPU_FREQ_GOV_GOVNAMEHERE
tristate "'gov_name_lowercase' cpufreq governor"
depends on CPU_FREQ
help
governor' - a custom governor!
config CPU_FREQ_GOV_SMARTASS2
tristate "'smartassV2' cpufreq governor"
depends on CPU_FREQ
help
'smartassV2' - a "smart" optimized governor!
next to adding the choice, you also must declare the possibility that the governor gets chosen as
default governor.
config CPU_FREQ_DEFAULT_GOV_GOVNAMEHERE
bool "gov_name_lowercase"
select CPU_FREQ_GOV_GOVNAMEHERE
help
Use the CPUFreq governor 'govname' as default.
config CPU_FREQ_DEFAULT_GOV_SMARTASS2
bool "smartass2"
select CPU_FREQ_GOV_SMARTASS2
help
Use the CPUFreq governor 'smartassV2' as default.
– can’t find the right place to put it? Just search for “CPU_FREQ_GOV_CONSERVATIVE”, and place the
code beneath, same thing counts for “CPU_FREQ_DEFAULT_GOV_CONSERVATIVE”
Now that Kconfig is finished you can save and close the file.
3. While still in the /drivers/cpufreq folder, open Makefile. In Makefile, add the line
corresponding to your CPU Governor. for example:
obj-$(CONFIG_CPU_FREQ_GOV_SMARTASS2) += cpufreq_smartass2.o
Be ware that you do not call the native C file, but the O file! which is the compiled C file. Save the
file.
4. Move to: kernel_source/includes/linux. Now open cpufreq.h Scroll down until you see
something like:
https://riptutorial.com/ 115
#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND)
extern struct cpufreq_governor cpufreq_gov_ondemand;
#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_ondemand)
Now add your entry with the selected CPU Governor, example:
#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2)
extern struct cpufreq_governor cpufreq_gov_smartass2;
#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_smartass2)
The initial CPU Governor setup is now complete. when you’ve done all steps successfully, you
should be able to choose your governor from the menu (menuconfig, xconfig, gconfig, nconfig).
Once checked in the menu it will be included to the kernel.
Commit that is nearly the same as above instructions: Add smartassV2 and lulzactive governor
commit
I/O Schedulers
You can enhance your kernel by adding new I/O schedulers if needed. Globally, governors and
schedulers are the same; they both provide a way how the system should work. However, for the
schedulers it is all about the input/output datastream except for the CPU settings. I/O schedulers
decide how an upcoming I/O activity will be scheduled. The standard schedulers such as noop or
cfq are performing very reasonably.
1. Copy your I/O scheduler file (for example, sio-iosched.c) and browse to kernel_source/block.
Paste the scheduler file there.
2. Now open Kconfig.iosched and add your choice to the Kconfig, for example for SIO:
config IOSCHED_SIO
tristate "Simple I/O scheduler"
default y
---help---
The Simple I/O scheduler is an extremely simple scheduler,
based on noop and deadline, that relies on deadlines to
ensure fairness. The algorithm does not do any sorting but
basic merging, trying to keep a minimum overhead. It is aimed
mainly for aleatory access devices (eg: flash devices).
https://riptutorial.com/ 116
4. Open the Makefile in kernel_source/block/ and simply add the following line for SIO:
obj-$(CONFIG_IOSCHED_SIO) += sio-iosched.o
Save the file and you are done! The I/O schedulers should now pop up at the menu config.
https://riptutorial.com/ 117
Chapter 19: Android NDK
Examples
Building native executables for Android
project/jni/main.c
#include <stdio.h>
#include <unistd.h>
int main(void) {
printf("Hello world!\n");
return 0;
}
project/jni/Android.mk
include $(CLEAR_VARS)
LOCAL_MODULE := hello_world
LOCAL_SRC_FILES := main.c
include $(BUILD_EXECUTABLE)
project/jni/Application.mk
APP_ABI := all
APP_PLATFORM := android-21
If you want to support devices running Android versions lower than 5.0 (API 21), you need to
compile your binary with APP_PLATFORM set to an older API, e.g. android-8. This is a consequence of
Android 5.0 enforcing Position Independent Binaries (PIE), whereas older devices do not
necessarily support PIEs. Therefore, you need to use either the PIE or the non-PIE, depending on
the device version. If you want to use the binary from within your Android application, you need to
check the API level and extract the correct binary.
APP_ABI can be changed to specific platforms such as armeabi to build the binary for those
architectures only.
In the worst case, you will have both a PIE and a non-PIE binary for each architecture (about 14
different binaries using ndk-r10e).
cd project
ndk-build
https://riptutorial.com/ 118
You will find the binaries at project/libs/<architecture>/hello_world. You can use them via ADB (
push and chmod it with executable permission) or from your application (extract and chmod it with
executable permission).
To determine the architecture of the CPU, retrieve the build property ro.product.cpu.abi for the
primary architecture or ro.product.cpu.abilist (on newer devices) for a complete list of supported
architectures. You can do this using the android.os.Build class from within your application or
using getprop <name> via ADB.
ndk-build clean
First make sure you link against the logging library in your Android.mk file:
LOCAL_LDLIBS := -llog
#include <android/log.h>
#define TAG "MY LOG"
Example:
int x = 42;
LOGD("The value of x is %d", x);
https://riptutorial.com/ 119
Read Android NDK online: https://riptutorial.com/android/topic/492/android-ndk
https://riptutorial.com/ 120
Chapter 20: Android Paypal Gateway
Integration
Remarks
Paypal provide us their own library for payment so it is now much secure and easy to implement in
our application. Below are the important step to do.
Examples
Setup paypal in your android code
2)Now open your manifest file and give the below permissions
<service
android:name="com.paypal.android.sdk.payments.PayPalService"
android:exported="false" />
<activity android:name="com.paypal.android.sdk.payments.PaymentActivity" />
<activity android:name="com.paypal.android.sdk.payments.LoginActivity" />
<activity android:name="com.paypal.android.sdk.payments.PaymentMethodActivity" />
<activity android:name="com.paypal.android.sdk.payments.PaymentConfirmActivity" />
<activity android:name="com.paypal.android.sdk.payments.PayPalFuturePaymentActivity" />
<activity android:name="com.paypal.android.sdk.payments.FuturePaymentConsentActivity" />
<activity android:name="com.paypal.android.sdk.payments.FuturePaymentInfoActivity" />
<activity
android:name="io.card.payment.CardIOActivity"
android:configChanges="keyboardHidden|orientation" />
<activity android:name="io.card.payment.DataEntryActivity" />
4)Open your Activity class and set Configuration for your app-
5)Now set client id from the Paypal developer account- private static final String
CONFIG_CLIENT_ID = "PUT YOUR CLIENT ID"; 6)Inside onCreate method call the Paypal
service- Intent intent = new Intent(this, PayPalService.class);
intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config); startService(intent);
7)Now you are ready to make a payment just on button press call the Payment Activity-
https://riptutorial.com/ 121
PayPalPayment thingToBuy = new PayPalPayment(new BigDecimal(1),"USD", "androidhub4you.com",
PayPalPayment.PAYMENT_INTENT_SALE);
Intent intent = new Intent(MainActivity.this,
PaymentActivity.class);
intent.putExtra(PaymentActivity.EXTRA_PAYMENT, thingToBuy);
startActivityForResult(intent, REQUEST_PAYPAL_PAYMENT);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_PAYPAL_PAYMENT) {
if (resultCode == Activity.RESULT_OK) {
PaymentConfirmation confirm = data
.getParcelableExtra(PaymentActivity.EXTRA_RESULT_CONFIRMATION);
if (confirm != null) {
try {
System.out.println("Responseeee"+confirm);
Log.i("paymentExample", confirm.toJSONObject().toString());
JSONObject jsonObj=new
JSONObject(confirm.toJSONObject().toString());
String
paymentId=jsonObj.getJSONObject("response").getString("id");
System.out.println("payment id:-=="+paymentId);
Toast.makeText(getApplicationContext(), paymentId,
Toast.LENGTH_LONG).show();
} catch (JSONException e) {
Log.e("paymentExample", "an extremely unlikely failure
occurred: ", e);
}
}
} else if (resultCode == Activity.RESULT_CANCELED) {
Log.i("paymentExample", "The user canceled.");
} else if (resultCode == PaymentActivity.RESULT_EXTRAS_INVALID) {
Log.i("paymentExample", "An invalid Payment was submitted. Please see
the docs.");
}
}
https://riptutorial.com/ 122
Chapter 21: Android Places API
Examples
Place Picker Usage Example
Place Picker is a really simple UI widget provided by Places API. It provides a built-in map, current
location, nearby places, search abilities and autocomplete.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_place_picker_sample);
https://riptutorial.com/ 123
Getting Current Places by Using Places API
You can get the current location and local places of user by using the Google Places API.
Ar first, you should call the PlaceDetectionApi.getCurrentPlace() method in order to retrieve local
business or other places. This method returns a PlaceLikelihoodBuffer object which contains a list
of PlaceLikelihood objects. Then, you can get a Place object by calling the
PlaceLikelihood.getPlace() method.
Important: You must request and obtain the ACCESS_FINE_LOCATION permission in order to
allow your app to access precise location information.
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_location);
getCurrentLocation();
}
ActivityCompat.requestPermissions(this,new
String[]{Manifest.permission.ACCESS_FINE_LOCATION},PERMISSION_REQUEST_TO_ACCESS_LOCATION);
return;
}
PendingResult<PlaceLikelihoodBuffer> result =
Places.PlaceDetectionApi.getCurrentPlace(googleApiClient, null);
result.setResultCallback(new ResultCallback<PlaceLikelihoodBuffer>() {
@Override
public void onResult(PlaceLikelihoodBuffer likelyPlaces) {
Log.i(LOG_TAG, String.format("Result received : %d " , likelyPlaces.getCount() ));
StringBuilder stringBuilder = new StringBuilder();
https://riptutorial.com/ 124
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[]
grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_TO_ACCESS_LOCATION: {
// If the request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] ==
PackageManager.PERMISSION_GRANTED) {
getCurrentLocation();
} else {
// Permission denied, boo!
// Disable the functionality that depends on this permission.
}
return;
}
// Add further 'case' lines to check for other permissions this app might request.
}
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.e(LOG_TAG, "GoogleApiClient connection failed: " +
connectionResult.getErrorMessage());
}
The autocomplete feature in the Google Places API for Android provides place predictions to user.
While user types in the search box, autocomplete shows places according to user's queries.
AutoCompleteActivity.java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_autocomplete);
autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
@Override
public void onPlaceSelected(Place place) {
Log.i(LOG_TAG, "Place: " + place.getName());
txtSelectedPlaceName.setText(String.format("Selected places : %s - %s" ,
place.getName(), place.getAddress()));
}
@Override
https://riptutorial.com/ 125
public void onError(Status status) {
Log.i(LOG_TAG, "An error occurred: " + status);
Toast.makeText(AutoCompleteActivity.this, "Place cannot be selected!!",
Toast.LENGTH_SHORT).show();
}
});
activity_autocomplete.xml
<fragment
android:id="@+id/fragment_autocomplete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:name="com.google.android.gms.location.places.ui.PlaceAutocompleteFragment"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtSelectedPlaceName"
android:layout_margin="20dp"
android:padding="15dp"
android:hint="@string/txt_select_place_hint"
android:textSize="@dimen/place_autocomplete_prediction_primary_text"/>
fromPlaceEdit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
//Do your stuff from place
startActivityForResult(intent,
PLACE_AUTOCOMPLETE_FROM_PLACE_REQUEST_CODE);
} catch (GooglePlayServicesRepairableException e) {
// TODO: Handle the error.
} catch (GooglePlayServicesNotAvailableException e) {
// TODO: Handle the error.
}
}
});
toPlaceEdit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
//Do your stuff to place
https://riptutorial.com/ 126
startActivityForResult(intent, PLACE_AUTOCOMPLETE_TO_PLACE_REQUEST_CODE);
} catch (GooglePlayServicesRepairableException e) {
// TODO: Handle the error.
} catch (GooglePlayServicesNotAvailableException e) {
// TODO: Handle the error.
}
}
});
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PLACE_AUTOCOMPLETE_FROM_PLACE_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
//Do your ok >from place< stuff here
} else if (resultCode == PlaceAutocomplete.RESULT_ERROR) {
//Handle your error >from place<
} else if (resultCode == RESULT_CANCELED) {
// The user canceled the operation.
}
} else if (requestCode == PLACE_AUTOCOMPLETE_TO_PLACE_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
//Do your ok >to place< stuff here
} else if (resultCode == PlaceAutocomplete.RESULT_ERROR) {
//Handle your error >to place<
} else if (resultCode == RESULT_CANCELED) {
// The user canceled the operation.
}
}
}
In some scenarios, we might want to narrow down the results being shown by
PlaceAutocomplete to a specific country or maybe to show only Regions. This can be achieved
by setting an AutocompleteFilter on the intent. For example, if I want to look only for places of
type REGION and only belonging to India, I would do the following:
MainActivity.java
Intent intent =
new PlaceAutocomplete.IntentBuilder(PlaceAutocomplete.MODE_FULLSCREEN)
.setFilter(typeFilter)
https://riptutorial.com/ 127
.build(this);
startActivityForResult(intent, PLACE_AUTOCOMPLETE_REQUEST_CODE);
} catch (GooglePlayServicesRepairableException
| GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
}
}
activity_main.xml
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/selected_place"/>
</LinearLayout>
The PlaceAutocomplete will launch automatically and you can then select a place from the
results which will only be of the type REGION and will only belong to the specified country. The
intent can also be launched at the click of a button.
https://riptutorial.com/ 128
Chapter 22: Android programming with Kotlin
Introduction
Using Kotlin with Android Studio is an easy task as Kotlin is developed by JetBrains. It is the same
company that stands behind IntelliJ IDEA - a base IDE for Android Studio. That is why there are
almost none problems with the compatibility.
Remarks
If you want to learn more about Kotlin Programming Language check out Documentation.
Examples
Installing the Kotlin plugin
For Windows:
For Mac:
And then search for and install Kotlin. You'll need to restart the IDE after this completes.
https://riptutorial.com/ 129
https://riptutorial.com/ 130
and then add Kotlin support to it or modify your existing project. To do it, you have to:
1. Add dependency to a root gradle file - you have to add the dependency for kotlin-android
plugin to a root build.gradle file.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.2'
}
}
allprojects {
repositories {
jcenter()
}
}
2. Apply Kotlin Android Plugin - simply add apply plugin: 'kotlin-android' to a module
build.gradle file.
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "org.example.example"
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
https://riptutorial.com/ 131
compile 'org.jetbrains.kotlin:kotlin-stdlib:1.1.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:appcompat-v7:25.3.1'
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile 'junit:junit:4.12'
}
https://riptutorial.com/ 132
Final class could look like this:
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
https://riptutorial.com/ 133
Converting existing Java code to Kotlin
Kotlin Plugin for Android Studio support converting existing Java files to Kotlin files. Choose a
Java file and invoke action Convert Java File to Kotlin File:
fun startNewActivity(){
val intent: Intent = Intent(context, Activity::class.java)
startActivity(intent)
}
fun startNewActivityWithIntents(){
val intent: Intent = Intent(context, Activity::class.java)
intent.putExtra(KEY_NAME, KEY_VALUE)
startActivity(intent)
}
https://riptutorial.com/ 134
Chapter 23: Android Sound and Media
Examples
How to pick image and video for api >19
Here is a tested code for image and video.It will work for all APIs less than 19 and greater than 19
as well.
Image:
Video:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
if (requestCode == 10) {
Uri selectedImageUri = data.getData();
String selectedImagePath = getRealPathFromURI(selectedImageUri);
} else if (requestCode == 20) {
Uri selectedVideoUri = data.getData();
String selectedVideoPath = getRealPathFromURI(selectedVideoUri);
}
https://riptutorial.com/ 135
if (uri == null) {
return null;
}
String[] projection = {MediaStore.Images.Media.DATA};
Cursor cursor = getActivity().getContentResolver().query(uri, projection, null,
null, null);
if (cursor != null) {
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
return uri.getPath();
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// Getting the user sound settings
AudioManager audioManager = (AudioManager)
getSystemService(AUDIO_SERVICE);
float actualVolume = (float) audioManager
.getStreamVolume(AudioManager.STREAM_MUSIC);
float maxVolume = (float) audioManager
.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
float volume = actualVolume / maxVolume;
// Is the sound loaded already?
if (loaded) {
soundPool.play(soundID, volume, volume, 1, 0, 1f);
Log.e("Test", "Played sound");
https://riptutorial.com/ 136
}
}
return false;
}
}
https://riptutorial.com/ 137
Chapter 24: Android Studio
Examples
Filter logs from UI
Android logs can be filtered directly from the UI. Using this code
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e(TAG1,"Log from onCreate method with TAG1");
Log.i(TAG2,"Log from onCreate method with TAG2");
}
}
The level can be set to get logs with given level and above. For example the verbose level will
catch verbose, debug, info, warn, error and assert logs.
Using the same example, if I set the level to error, I only get
https://riptutorial.com/ 138
01-14 10:34:46.961 12880-12880/androdi.doc.so.thiebaudthomas.sodocandroid E/MainActivity: Log
from onCreate method with TAG1
Custom filters can be set and save from the UI. In the AndroidMonitor tab, click on the right
dropdown (must contains Show only selected application or No filters) and select Edit filter
configuration.
https://riptutorial.com/ 139
Important If you add an input in the filter bar, android studio will consider both your filter and your
input.
https://riptutorial.com/ 140
Custom colors of logcat message based on message importance
Go to File -> Settings -> Editor -> Colors & Fonts -> Android Logcat
https://riptutorial.com/ 141
Enable/Disable blank line copy
ctrl + alt + shift + / (cmd + alt + shift + / on MacOS) should show you the following dialog:
https://riptutorial.com/ 142
The key you want to enable/disable is
editor.skip.copy.and.cut.for.empty.selection
These are based on the default IntelliJ shortcut map. You can switch to other common IDE
shortcut maps via File -> Settings -> Keymap -> <Choose Eclipse/Visual Studio/etc from Keymaps
dropdown>
https://riptutorial.com/ 143
Action Shortcut
Build CTRL + F9
Find CTRL +F
Refactor:
Action Shortcut
Refactor This (menu/picker for all applicable refactor Mac CTRL + T - Win/Linux CTRL +
actions of the current element) ALT + T
Rename SHIFT + F6
https://riptutorial.com/ 144
Action Shortcut
1. Click File -> Settings. Search for "gradle" and click in Offline work box.
2. Go to Compiler (in same settings dialog just below Gradle) and add --offline to Command-line
Options text box.
org.gradle.daemon=true
org.gradle.parallel=true
-Xms1024m
-Xmx4096m
-XX:MaxPermSize=1024m
-XX:ReservedCodeCacheSize=256m
-XX:+UseCompressedOops
Window
%USERPROFILE%.{FOLDER_NAME}\studio.exe.vmoptions and/or
%USERPROFILE%.{FOLDER_NAME}\studio64.exe.vmoptions
Mac
~/Library/Preferences/{FOLDER_NAME}/studio.vmoptions
Linux
~/.{FOLDER_NAME}/studio.vmoptions and/or
~/.{FOLDER_NAME}/studio64.vmoptions
https://riptutorial.com/ 145
System Requirements
Installation
Window
Linux
By going to Settings >> Keymap A window will popup showing All the Editor Actions with the their
name and shortcuts. Some of the Editor Actions do not have shortcuts. So right click on that and
add a new shortcut to that.
Check the image below
https://riptutorial.com/ 146
Gradle build project takes forever
Android Studio -> Preferences -> Gradle -> Tick Offline work and then restart your Android
studio.
Reference screenshot:
https://riptutorial.com/ 147
https://riptutorial.com/ 148
• Assets folder will be under MAIN folder with the same symbol as RES folder.
• In this example I put a font file.
https://riptutorial.com/ 149
Chapter 25: Android Things
Examples
Controlling a Servo Motor
This example assumes you have a servo with the following characteristics, which happen to be
typical:
You need to check if those values match your hardware, since forcing it to go outside its specified
operating range can damage the servo. A damaged servo in turn has the potential to damage your
Android Things device. The example ServoController class consists of two methods, setup() and
setPosition():
pin.setPwmFrequencyHz(1000.0d / periodMs);
setPosition(90);
pin.setEnabled(true);
}
Log.i(TAG, "Duty cycle = " + dutyCycle + " pulse length = " + pulseLengthMs);
try {
pin.setPwmDutyCycle(dutyCycle);
} catch (IOException e) {
e.printStackTrace();
https://riptutorial.com/ 150
}
}
}
You can discover pin names that support PWM on your device as follows:
In order to make your servo swinging forever between 80 degrees and 100 degrees, you can
simply use the following code:
You can compile and deploy all of the above code without actually hooking any servo motors to
the computing device. For the wiring, refer to your computing device pinout chart (e.g. a Raspberry
Pi 3 pinout chart is available here).
Then you need to hook your servo to Vcc, Gnd, and signal.
https://riptutorial.com/ 151
Chapter 26: Android Versions
Remarks
Android API-
Name Release date Build.VERSION_CODES
version level
Battenberg 9 February
1.1 2 BASE_1_1
(Beta) 2009
15 September
Donut 1.6 4 DONUT
2009
26 October
Eclair 2.0 5 ECLAIR
2009
3 December
2.0.1 6 ECLAIR_0_1
2009
12 January
2.1 7 ECLAIR_MR1
2010
6 December
Gingerbread 2.3 9 GINGERBREAD
2010
9 February
2.3.3 10 GINGERBREAD_MR1
2011
22 February
Honeycomb 3.0 11 HONEYCOMB
2011
16 December
4.0.3 15 ICE_CREAM_SANDWICH_MR1
2011
https://riptutorial.com/ 152
Android API-
Name Release date Build.VERSION_CODES
version level
13 November
4.2 17 JELLY_BEAN_MR1
2012
31 October
KitKat 4.4 19 KITKAT
2013
17 October
Lollipop 5.0 21 LOLLIPOP
2014
5 October
Marshmallow 6.0 23 M
2015
22 August
Nougat 7.0 24 N
2016
5 December
7.1.1 25 N_MR1
2016
Examples
Checking the Android Version on device at runtime
In order to conditionally run code based on the device's Android version, use the TargetApi
annotation to avoid Lint errors, and check the build version before running the code specific to the
API level.
Here is an example of how to use a class that was introduced in API-23, in a project that supports
API levels lower than 23:
@Override
@TargetApi(23)
public void onResume() {
super.onResume();
if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
//run Marshmallow code
FingerprintManager fingerprintManager =
this.getSystemService(FingerprintManager.class);
https://riptutorial.com/ 153
//......................
}
}
https://riptutorial.com/ 154
Chapter 27: Android Vk Sdk
Examples
Initialization and login
5. Add recieved fingerprint into your Signing certificate fingerprint for Android: field in Vk
app settings (where you entered your package name)
compile 'com.vk:androidsdk:1.6.5'
8. Initialize the SDK on startup using the following method. The best way is to call it in the
Applications onCreate method.
This is the best way to initizlize VKSdk. Don't use the methid where VK_ID should be placed
inside strings.xml because api will not work correctly after it.
https://riptutorial.com/ 155
VKScope.STATUS,
VKScope.STATS,
VKScope.PHOTOS
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
someButtonForLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
VKSdk.login(this, VK_SCOPES);
}
});
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
VKSdk.onActivityResult(requestCode, resultCode, data, new VKCallback<VKAccessToken>()
{
@Override
public void onResult(VKAccessToken res) {
res.accessToken; //getting our token here.
}
@Override
public void onError(VKError error) {
Toast.makeText(SocialNetworkChooseActivity.this,
"User didn't pass Authorization", Toast.LENGTH_SHORT).show();
}
});
}
https://riptutorial.com/ 156
Chapter 28: Android-x86 in VirtualBox
Introduction
The idea of this section is to cover how to install and use the VirtualBox with Android-x86 for
debugging purposes. This is a difficult task because there are differences between versions. For
the moment I´m going to cover 6.0 which is the one that I had to work with and then we'll have to
find similarities.
It doesn't cover VirtualBox or a Linux in detail but it shows the commands I've used to make it
work.
Examples
Virtual Machine setup
• OS Type: Linux 2.6 (I've user 64bit because my computer can support it)
• Virtual hard drive size: 4Gb
• Ram Memory: 2048
• Video Memory: 8M
• Sound device: Sound Blaster 16.
• Network device: PCnet-Fast III, attached to NAT. You can also use bridged adapter, but you
need a DHCP server in your environment.
The image used with this configuration has been android-x86_64-6.0-r3.iso (it is 64bit)
downloaded from http://www.android-x86.org/download. I suppose that it also works with 32bit
version.
With the virtual hard drive just created, boot the virtual machine with the android-x86 image in the
optical drive.
https://riptutorial.com/ 157
Once you boot, you can see the grub menu of the Live CD
https://riptutorial.com/ 158
Choose the Debug Mode Option, then you should see the shell prompt. This is a busybox shell.
You can get more shell by switching between virtual console Alt-F1/F2/F3.
Create two partitions by fdisk (some other versions would use cfdisk). Format them to ext3. Then
reboot:
# fdisk /dev/sda
Then type:
"261" (choose a cylinder, we'll leave 50% of the disk for a 2nd partition)
https://riptutorial.com/ 159
"262" (262nd cylinder)
#mdev -s
#mke2fs -j -L DATA /dev/sda1
#mke2fs -j -L SDCARD /dev/sda2
#reboot -f
When you restart the virtual machine and the grub menu appears and you will be able edit the
kernel boot line so you can add DATA=sda1 SDCARD=sda2 options to point to the sdcard or the data
partition.
Installation in partition
With the virtual hard drive just created, boot the virtual machine with the android-x86 image as the
optical drive.
In the booting options of the Live CD choose "Installation - Install Android to hard disk"
https://riptutorial.com/ 160
Choose the sda1 partition and install android and we'll install grub.
Reboot the virtual machine but make sure that the image is not in the optical drive so it can restart
from the virtual hard drive.
In the grub menu we need to edit kernel like in the "Android-x86 6.0-r3" option so press e.
https://riptutorial.com/ 161
Then we substitute "quiet" with "vga=ask" and add the option "SDCARD=sda2"
Press b to boot, then you'll be able to choose the screen size pressing ENTER (the vga=ask option)
https://riptutorial.com/ 162
Once the installation wizard has started choose the language. I could choose English (United
States) and Spanish (United States) and I had trouble choosing any other.
https://riptutorial.com/ 163
Chapter 29: Animated AlertDialog Box
Introduction
Animated Alert Dialog Which display with some animation effects.. You Can Get Some Animation
for dialog box like Fadein, Slideleft, Slidetop, SlideBottom, Slideright, Fall, Newspager, Fliph, Flipv,
RotateBottom, RotateLeft, Slit, Shake, Sidefill to make Your application attractive..
Examples
Put Below code for Animated dialog...
animated_android_dialog_box.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#1184be"
android:onClick="animatedDialog1"
android:text="Animated Fall Dialog"
android:textColor="#fff" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
android:background="#1184be"
android:onClick="animatedDialog2"
android:text="Animated Material Flip Dialog"
android:textColor="#fff" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#1184be"
android:onClick="animatedDialog3"
android:text="Animated Material Shake Dialog"
android:textColor="#fff" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
android:background="#1184be"
android:onClick="animatedDialog4"
android:text="Animated Slide Top Dialog"
https://riptutorial.com/ 164
android:textColor="#fff" />
AnimatedAndroidDialogExample.java
NiftyDialogBuilder materialDesignAnimatedDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.animated_android_dialog_box);
materialDesignAnimatedDialog = NiftyDialogBuilder.getInstance(this);
}
https://riptutorial.com/ 165
.withDialogColor("#1c90ec")
.withButton1Text("OK")
.withButton2Text("Cancel")
.withDuration(700)
.withEffect(Effectstype.Slidetop)
.show();
}
}
build.gradle
dependencies {
compile 'com.nineoldandroids:library:2.4.0'
compile 'com.github.sd6352051.niftydialogeffects:niftydialogeffects:1.0.0@aar'
https://riptutorial.com/ 166
Chapter 30: Animators
Examples
Shake animation of an ImageView
Under res folder, create a new folder called "anim" to store your animation resources and put this
on that folder.
shakeanimation.xml
activity_landing.xml
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imgBell"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@mipmap/ic_notifications_white_48dp"/>
</RelativeLayout>
Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext=this;
setContentView(R.layout.activity_landing);
AnimateBell();
}
https://riptutorial.com/ 167
Animation shake = AnimationUtils.loadAnimation(mContext, R.anim.shakeanimation);
ImageView imgBell= (ImageView) findViewById(R.id.imgBell);
imgBell.setImageResource(R.mipmap.ic_notifications_active_white_48dp);
imgBell.setAnimation(shake);
}
In order to get a view to slowly fade in or out of view, use an ObjectAnimator. As seen in the code
below, set a duration using .setDuration(millis) where the millis parameter is the duration (in
milliseconds) of the animation. In the below code, the views will fade in / out over 500
milliseconds, or 1/2 second. To start the ObjectAnimator's animation, call .start(). Once the
animation is complete, onAnimationEnd(Animator animation) is called. Here is a good place to set
your view's visibility to View.GONE or View.VISIBLE.
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
fadeOut.setDuration(500);
fadeOut.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// We wanna set the view to GONE, after it's fade out. so it actually disappear
from the layout & don't take up space.
viewToFadeOut.setVisibility(View.GONE);
}
});
fadeOut.start();
}
fadeIn.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStar(Animator animation) {
// We wanna set the view to VISIBLE, but with alpha 0. So it appear invisible in
the layout.
viewToFadeIn.setVisibility(View.VISIBLE);
viewToFadeIn.setAlpha(0);
}
});
fadeIn.start();
}
TransitionDrawable animation
This example displays a transaction for an image view with only two images.(can use more
images as well one after the other for the first and second layer positions after each transaction as
https://riptutorial.com/ 168
a loop)
<resources>
<array
name="splash_images">
<item>@drawable/spash_imge_first</item>
<item>@drawable/spash_img_second</item>
</array>
</resources>
@SuppressWarnings("ResourceType")
Drawable drawable = icons.getDrawable(0); // ending image
@SuppressWarnings("ResourceType")
Drawable drawableTwo = icons.getDrawable(1); // starting image
ValueAnimator
ValueAnimator introduces a simple way to animate a value (of a particular type, e.g. int, float, etc.).
https://riptutorial.com/ 169
There are two ways you can create the ValueAnimator:
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="250"
android:valueFrom="20"
android:valueTo="40"
android:valueType="floatType"/>
ObjectAnimator
ObjectAnimator is a subclass of ValueAnimator with the added ability to set the calculated value to
the property of a target View.
Just like in the ValueAnimator, there are two ways you can create the ObjectAnimator:
(the example code animates an alpha of a View from 0.4f to 0.2f in 250ms)
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="250"
android:propertyName="alpha"
android:valueFrom="0.4"
android:valueTo="0.2"
android:valueType="floatType"/>
https://riptutorial.com/ 170
ObjectAnimator animator = (ObjectAnimator) AnimatorInflater.loadAnimator(context,
R.animator.example_animator);
animator.setTarget(exampleView);
// set all the animation-related stuff you want (interpolator etc.)
animator.start();
2. From code:
ViewPropertyAnimator
Every single View has a ViewPropertyAnimator object available through the animate() method. You
can use that to animate multiple properties at once with a simple call. Every single method of a
ViewPropertyAnimator specifies the target value of a specific parameter that the
ViewPropertyAnimator should animate to.
Note: Calling start() on a ViewPropertyAnimator object is NOT mandatory. If you don't do that
you're just letting the platform to handle the starting of the animation in the appropriate time (next
animation handling pass). If you actually do that (call start()) you're making sure the animation is
started immediately.
v.getLayoutParams().height = 0;
v.setVisibility(View.VISIBLE);
Animation a = new Animation()
{
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
v.getLayoutParams().height = interpolatedTime == 1
? LayoutParams.WRAP_CONTENT
: (int)(targtetHeight * interpolatedTime);
https://riptutorial.com/ 171
v.requestLayout();
}
@Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration((int)(targtetHeight /
v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
@Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration((int)(initialHeight /
v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
}
https://riptutorial.com/ 172
Chapter 31: Annotation Processor
Introduction
Annotation processor is a tool build in javac for scanning and processing annotations at compile
time.
Annotations are a class of metadata that can be associated with classes, methods, fields, and
even other annotations.There are two ways to access these annotations at runtime via reflection
and at compile time via annotation processors.
Examples
@NonNull Annotation
Here @NonNull is annotation which is processed compile time by the android studio to warn you
that the particular function needs non null parameter.
Types of Annotations
@interface CustomAnnotation {}
@interface CustomAnnotation {
int value();
}
@interface CustomAnnotation{
int value1();
String value2();
String value3();
}
https://riptutorial.com/ 173
Creating and Using Custom Annotations
• Target - on which these annotations will work on like field level, method level, type level etc.
• Retention - to what level annotation will be available.
For this, we have built in custom annotations. Check out these mostly used ones:
@Target
@Retention
https://riptutorial.com/ 174
Creating Custom Annotation
class Foo{
@CustomAnnotation(value = 1) // will be used by an annotation processor
public void foo(){..}
}
https://riptutorial.com/ 175
Chapter 32: AsyncTask
Parameters
Parameter Details
Params the type of the parameters sent to the task upon execution.
Progress the type of the progress units published during the background computation
Examples
Basic Usage
In Android Activities and Services, most callbacks are run on the main thread. This makes it
simple to update the UI, but running processor- or I/O-heavy tasks on the main thread can cause
your UI to pause and become unresponsive (official documentation on what then happens).
You can remedy this by putting these heavier tasks on a background thread.
One way to do this is using an AsyncTask, which provides a framework to facilitate easy usage of
a background Thread, and also perform UI Thread tasks before, during, and after the background
Thread has completed its work.
Example
public class MyCustomAsyncTask extends AsyncTask<File, Void, String> {
@Override
protected void onPreExecute(){
// This runs on the UI thread before the background thread executes.
super.onPreExecute();
// Do pre-thread tasks such as initializing variables.
Log.v("myBackgroundTask", "Starting Background Task");
}
https://riptutorial.com/ 176
@Override
protected String doInBackground(File... params) {
// Disk-intensive work. This runs on a background thread.
// Search through a file for the first line that contains "Hello", and return
// that line.
try (Scanner scanner = new Scanner(params[0])) {
while (scanner.hasNextLine()) {
final String line = scanner.nextLine();
publishProgress(); // tell the UI thread we made progress
if (line.contains("Hello")) {
return line;
}
}
return null;
}
}
@Override
protected void onProgressUpdate(Void...p) {
// Runs on the UI thread after publishProgress is invoked
Log.v("Read another line!")
}
@Override
protected void onPostExecute(String s) {
// This runs on the UI thread after complete execution of the doInBackground() method
// This function receives result(String s) returned from the doInBackground() method.
// Update UI with the found string.
TextView view = (TextView) findViewById(R.id.found_string);
if (s != null) {
view.setText(s);
} else {
view.setText("Match not found.");
}
}
Usage:
MyCustomAsyncTask asyncTask = new MyCustomAsyncTask<File, Void, String>();
// Run the task with a user supplied filename.
asyncTask.execute(userSuppliedFilename);
or simply:
new MyCustomAsyncTask().execute(userSuppliedFilename);
Note
When defining an AsyncTask we can pass three types between < > brackets.
https://riptutorial.com/ 177
Defined as <Params, Progress, Result> (see Parameters section)
Note that you can't pass primitive types (i.e. int, float and 6 others) as parameters. In such cases,
you should pass their wrapper classes, e.g. Integer instead of int, or Float instead of float.
AsyncTasks don't follow Activity instances' life cycle. If you start an AsyncTask inside an Activity
and you rotate the device, the Activity will be destroyed and a new instance will be created. But
the AsyncTask will not die. It will go on living until it completes.
Solution: AsyncTaskLoader
One subclass of Loaders is the AsyncTaskLoader. This class performs the same function as the
AsyncTask, but much better. It can handle Activity configuration changes more easily, and it
behaves within the life cycles of Fragments and Activities. The nice thing is that the
AsyncTaskLoader can be used in any situation that the AsyncTask is being used. Anytime data
needs to be loaded into memory for the Activity/Fragment to handle, The AsyncTaskLoader can
do the job better.
Canceling AsyncTask
This doesn't stop your task if it was in progress, it just sets the cancelled flag which can be
checked by checking the return value of isCancelled() (assuming your code is currently running)
by doing this:
https://riptutorial.com/ 178
}
}
Note
If an AsyncTask is canceled while doInBackground(Params... params) is still executing then the
method onPostExecute(Result result) will NOT be called after doInBackground(Params... params)
returns. The AsyncTask will instead call the onCancelled(Result result) to indicate that the task
was cancelled during execution.
Publishing progress
Sometimes, we need to update the progress of the computation done by an AsyncTask. This
progress could be represented by a string, an integer, etc. To do this, we have to use two
functions. First, we need to set the onProgressUpdate function whose parameter type is the same as
the second type parameter of our AsyncTask.
Second, we have to use the function publishProgress necessarily on the doInBackground function,
and that is all, the previous method will do all the job.
This tutorial explains how to download Image using AsyncTask in Android. The example below
download image while showing progress bar while during download.
https://riptutorial.com/ 179
• AsyncTasks should ideally be used for short operations (a few seconds at the most.)
• An asynchronous task is defined by 3 generic types, called Params, Progress and Result,
and 4 steps, called onPreExecute(), doInBackground(), onProgressUpdate() and onPostExecute().
• In onPreExecute() you can define code, which need to be executed before background
processing starts.
• doInBackground have code which needs to be executed in background, here in
doInBackground() we can send results to multiple times to event thread by publishProgress()
method, to notify background processing has been completed we can return results simply.
• onProgressUpdate() method receives progress updates from doInBackground() method, which
is published via publishProgress() method, and this method can use this progress update to
update event thread
• onPostExecute() method handles results returned by doInBackground() method.
• The generic types used are
○Params, the type of the parameters sent to the task upon execution
○Progress, the type of the progress units published during the background computation.
○Result, the type of the result of the background computation.
• If an async task not using any types, then it can be marked as Void type.
• An running async task can be cancelled by calling cancel(boolean) method.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/downloadButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Click Here to Download" />
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="Your image will appear here" />
</LinearLayout>
.java class
package com.javatechig.droid;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
https://riptutorial.com/ 180
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.asynch);
Button imageDownloaderBtn = (Button) findViewById(R.id.downloadButton);
imageDownloaderBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
new ImageDownloader().execute(downloadUrl);
}
});
}
@Override
protected Bitmap doInBackground(String... param) {
// TODO Auto-generated method stub
return downloadBitmap(param[0]);
}
@Override
protected void onPreExecute() {
Log.i("Async-Example", "onPreExecute Called");
simpleWaitDialog = ProgressDialog.show(ImageDownladerActivity.this,
"Wait", "Downloading Image");
@Override
protected void onPostExecute(Bitmap result) {
Log.i("Async-Example", "onPostExecute Called");
downloadedImg.setImageBitmap(result);
simpleWaitDialog.dismiss();
https://riptutorial.com/ 181
}
if (statusCode != HttpStatus.SC_OK) {
Log.w("ImageDownloader", "Error " + statusCode +
" while retrieving bitmap from " + url);
return null;
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// You Could provide a more explicit error message for IOException
getRequest.abort();
Log.e("ImageDownloader", "Something went wrong while" +
" retrieving bitmap from " + url + e.toString());
}
return null;
}
}
}
Since there is currently no comment field for examples (or I haven't found it or I haven't permission
for it) here is some comment about this:
https://riptutorial.com/ 182
However the example currently has problems with
It is common for an AsyncTask to require a reference to the Activity that called it.
If the AsyncTask is an inner class of the Activity, then you can reference it and any member
variables/methods directly.
If, however, the AsyncTask is not an inner class of the Activity, you will need to pass an Activity
reference to the AsyncTask. When you do this, one potential problem that may occur is that the
AsyncTask will keep the reference of the Activity until the AsyncTask has completed its work in its
background thread. If the Activity is finished or killed before the AsyncTask's background thread
work is done, the AsyncTask will still have its reference to the Activity, and therefore it cannot be
garbage collected.
In order to prevent this from happening, make use of a WeakReference in the AsyncTask instead
of having a direct reference to the Activity.
@Override
protected void onPreExecute() {
final Activity activity = mActivity.get();
if (activity != null) {
....
}
}
@Override
protected Void doInBackground(String... params) {
//Do something
String param1 = params[0];
https://riptutorial.com/ 183
String param2 = params[1];
return null;
}
@Override
protected void onPostExecute(Void result) {
final Activity activity = mActivity.get();
if (activity != null) {
activity.updateUI();
}
}
}
Order of execution
When first introduced, AsyncTasks were executed serially on a single background thread. Starting
with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel.
Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors
caused by parallel execution.
SERIAL_EXECUTOR -> An Executor that executes tasks one at a time in serial order.
sample :
AsyncTask is an abstract Class and does not inherit the Thread class. It has an abstract method
doInBackground(Params... params), which is overridden to perform the task. This method is called
from AsyncTask.call().
https://riptutorial.com/ 184
Moreover, AsyncTask contains 2 Executors
THREAD_POOL_EXECUTOR
SERIAL_EXECUTOR
Both Executors are static, hence only one THREAD_POOL_EXECUTOR and one SerialExecutor objects
exist, but you can create several AsyncTask objects.
Therefore, if you try to do multiple background task with the default Executor (SerialExecutor),
these task will be queue and executed serially.
If you try to do multiple background task with THREAD_POOL_EXECUTOR, then they will be executed
parallelly.
Example:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt = (Button) findViewById(R.id.button);
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
BackgroundTask backgroundTask = new BackgroundTask ();
Integer data[] = { ++CountTask, null, null };
}
});
https://riptutorial.com/ 185
private class BackgroundTask extends AsyncTask<Integer, Integer, Integer> {
int taskNumber;
@Override
protected Integer doInBackground(Integer... integers) {
taskNumber = integers[0];
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
publishProgress(taskNumber);
return null;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Integer aLong) {
super.onPostExecute(aLong);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
Log.d(TAG, "Task = " + (int) values[0]
+ " Task Execution Completed");
}
}
}
Perform Click on button several times to start a task and see the result.
At t=36s, tasks 2, 3 and 4 are queued and started executing also because they are executing
parallelly.
https://riptutorial.com/ 186
08-02 19:48:**36.915**: D/AsyncTaskExample(11693): Task = 5 Task Queued
08-02 19:48:**36.915**: D/AsyncTaskExample(11693): Task = 5 Task Running in Background
08-02 19:48:37.025: D/AsyncTaskExample(11693): Task = 2 Task Execution Completed
08-02 19:48:37.165: D/AsyncTaskExample(11693): Task = 3 Task Execution Completed
----------
Comment Task Executed in thread pool (1) and uncomment Task executed Serially (2).
Perform Click on button several times to start a task and see the result.
It is executing the task serially hence every task is started after the current task completed
execution. Hence when Task 1's execution completes, only Task 2 starts running in background.
Vice versa.
https://riptutorial.com/ 187
Chapter 33: AudioManager
Examples
Requesting Transient Audio Focus
audioManager.requestAudioFocus(audioListener, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
audioManager.requestAudioFocus(audioListener, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
https://riptutorial.com/ 188
Chapter 34: AudioTrack
Examples
Generate tone of a specific frequency
To play a sound of with a specific tone,we first have to create a sine wave sound.This is done in
the following way.
Now we have to configure AudioTrack to play in accordance with the generated buffer . It is done
in the following manner
audioTrack.write(buffer, 0, buffer.length);
audioTrack.play();
https://riptutorial.com/ 189
Chapter 35: AutoCompleteTextView
Remarks
If you want to offer suggestions to the user when they type in an editable text field, you can use an
AutoCompleteTextView. It provides suggestions automatically when the user is typing. The list of
suggestions is displayed in a drop down menu from which the user can select one to replace the
contents of the edit box.
Examples
Simple, hard-coded AutoCompleteTextView
<AutoCompleteTextView
android:id="@+id/autoCompleteTextView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="65dp"
android:ems="10" />
Find the view in code after setContentView() (or its fragment or custom view equivalent):
Tip: Though the preferred way would be to provide data via a Loader of some kind instead of a
hard-coded list like this.
https://riptutorial.com/ 190
<AutoCompleteTextView
android:id="@+id/auto_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionThreshold="2"
android:hint="@string/hint_enter_name" />
</LinearLayout>
<TextView
android:id="@+id/lbl_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="16dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="16dp"
android:text="Medium Text"
android:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>
strings.xml
<resources>
<string name="hint_enter_name">Enter Name</string>
</resources>
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mList = retrievePeople();
txtSearch = (AutoCompleteTextView) findViewById(R.id.auto_name);
adapter = new PeopleAdapter(this, R.layout.activity_main, R.id.lbl_name, mList);
txtSearch.setAdapter(adapter);
txtSearch.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int pos, long id) {
//this is the way to find selected object/item
selectedPerson = (People) adapterView.getItemAtPosition(pos);
https://riptutorial.com/ 191
}
});
}
https://riptutorial.com/ 192
Adapter class : PeopleAdapter.java
Context context;
int resource, textViewResourceId;
List<People> items, tempItems, suggestions;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.row, parent, false);
}
People people = items.get(position);
if (people != null) {
TextView lblName = (TextView) view.findViewById(R.id.lbl_name);
if (lblName != null)
lblName.setText(people.getName());
}
return view;
}
@Override
public Filter getFilter() {
return nameFilter;
}
/**
* Custom Filter implementation for custom suggestions we provide.
*/
Filter nameFilter = new Filter() {
@Override
public CharSequence convertResultToString(Object resultValue) {
String str = ((People) resultValue).getName();
return str;
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
if (constraint != null) {
suggestions.clear();
for (People people : tempItems) {
if
(people.getName().toLowerCase().contains(constraint.toString().toLowerCase())) {
suggestions.add(people);
}
https://riptutorial.com/ 193
}
FilterResults filterResults = new FilterResults();
filterResults.values = suggestions;
filterResults.count = suggestions.size();
return filterResults;
} else {
return new FilterResults();
}
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
List<People> filterList = (ArrayList<People>) results.values;
if (results != null && results.count > 0) {
clear();
for (People people : filterList) {
add(people);
notifyDataSetChanged();
}
}
}
};
}
https://riptutorial.com/ 194
Chapter 36: Autosizing TextViews
Introduction
A TextView that automatically resizes text to fit perfectly within its bounds.
Android O allows you to instruct a TextView to let the size of the text expand or contract
automatically to fill its layout based on the TextView’s characteristics and boundaries.
There are two ways to set autosizing TextView: Granularity and Preset Sizes
Examples
Granularity
In Java:
In XML:
<TextView android:id=”@+id/autosizing_textview_presetsize”
android:layout_width=”wrap_content”
android:layout_height=”250dp”
android:layout_marginLeft=”0dp”
android:layout_marginTop=”0dp”
android:autoSizeMaxTextSize=”100sp”
android:autoSizeMinTextSize=”12sp”
android:autoSizeStepGranularity=”2sp”
android:autoSizeText=”uniform”
android:text=”Hello World!”
android:textSize=”100sp”
app:layout_constraintLeft_toLeftOf=”parent”
app:layout_constraintTop_toTopOf=”parent” />
Preset Sizes
In Java:
https://riptutorial.com/ 195
Call the setAutoSizeTextTypeUniformWithPresetSizes() method:
In XML:
<TextView android:id=”@+id/autosizing_textview_presetsize”
android:layout_width=”wrap_content”
android:layout_height=”250dp”
android:layout_marginLeft=”0dp”
android:layout_marginTop=”0dp”
android:autoSizeText=”uniform”
android:autoSizePresetSizes=”@array/autosize_text_sizes”
android:text=”Hello World!”
android:textSize=”100sp”
app:layout_constraintLeft_toLeftOf=”parent”
app:layout_constraintTop_toTopOf=”parent” />
To access the array as a resource, define the array in the res/values/arrays.xml file:
<array name=”autosize_text_sizes”>
<item>10sp</item>
<item>12sp</item>
<item>20sp</item>
<item>40sp</item>
<item>100sp</item>
</array>
https://riptutorial.com/ 196
Chapter 37: Barcode and QR code reading
Remarks
QRCodeReaderView
Zxing
Examples
Using QRCodeReaderView (based on Zxing)
QRCodeReaderView implements an Android view which show camera and notify when there's a
QR code inside the preview.
It uses the zxing open-source, multi-format 1D/2D barcode image processing library.
dependencies{
compile 'com.dlazaro66.qrcodereaderview:qrcodereaderview:2.0.0'
}
First use
• Add to your layout a QRCodeReaderView
<com.dlazaro66.qrcodereaderview.QRCodeReaderView
android:id="@+id/qrdecoderview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
https://riptutorial.com/ 197
private QRCodeReaderView qrCodeReaderView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_decoder);
@Override
protected void onResume() {
super.onResume();
qrCodeReaderView.startCamera();
}
@Override
protected void onPause() {
super.onPause();
qrCodeReaderView.stopCamera();
}
}
https://riptutorial.com/ 198
Chapter 38: Bitmap Cache
Introduction
Memory efficient bitmap caching: This is particularly important if your application uses animations
as they will be stopped during GC cleanup and make your application appears sluggish to the
user. A cache allows reusing objects which are expensive to create. If you load on object into
memory, you can think of this as a cache for the object.Working with bitmap in android is tricky.It is
more important to cache the bimap if you are going to use it repeatedly.
Syntax
• LruCache<String, Bitmap> mMemoryCache;//declaration of LruCache object.
• void addBitmapToMemoryCache(String key, Bitmap bitmap){}//declaration of generic method
adding bitmap into cache memory
• Bitmap getBitmapFromMemCache(String key){}//declaration of generic method for get bimap
from cache.
Parameters
Parameter Details
Examples
Bitmap Cache Using LRU Cache
LRU Cache
The following example code demonstrates a possible implementation of the LruCache class for
caching images.
https://riptutorial.com/ 199
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
https://riptutorial.com/ 200
Chapter 39: Bluetooth and Bluetooth LE API
Remarks
Bluetooth Classic is available from Android 2.0 (API level 5) and above. Bluetooth LE is available
from Android 4.3 (API level 18) and above.
Examples
Permissions
Add this permission to the manifest file to use Bluetooth features in your application:
If you need to initiate device discovery or manipulate Bluetooth settings, you also need to add this
permission:
Targetting Android API level 23 and above, will require location access:
* Also see the Permissions topic for more details on how to use permissions appropriately.
// ...
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
// ...
@Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent
data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == RESULT_OK) {
https://riptutorial.com/ 201
// Bluetooth was enabled
} else if (resultCode == RESULT_CANCELED) {
// Bluetooth was not enabled
}
}
}
// ...
// ...
@Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent
data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_DISCOVERABLE_BT) {
if (resultCode == RESULT_OK) {
// Device is discoverable
} else if (resultCode == RESULT_CANCELED) {
// Device is not discoverable
}
}
}
BluetoothAdapter mBluetoothAdapter;
//Device found
if (BluetoothDevice.ACTION_FOUND.equals(action))
{
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// Add the name and address to an array adapter to show in a list
mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
https://riptutorial.com/ 202
}
}
};
mBluetoothAdapter.startDiscovery();
unregisterReceiver(mReceiver);
After you obtained BluetoothDevice, you can communicate with it. This kind of communication
performed by using socket input\output streams:
1) Initialize socket:
2) Connect to socket:
try {
_socket.connect();
} catch (IOException connEx) {
try {
_socket.close();
} catch (IOException closeException) {
//Error
}
}
https://riptutorial.com/ 203
3) Obtaining socket Input\Output streams
Input stream - Used as incoming data channel (receive data from connected device)
Output stream - Used as outgoing data channel (send data to connected device)
After finishing 3rd step, we can receive and send data between both devices using
previously initialized streams:
while (true) {
try {
//reading data from input stream
bytesCount = _inStream.read(buffer);
if(buffer != null && bytesCount > 0)
{
//Parse received bytes
}
} catch (IOException e) {
//Error
}
}
The BluetoothLE API was introduced in API 18. However, the way of scanning devices has
https://riptutorial.com/ 204
changed in API 21. The searching of devices must start with defining the service UUID that is to
be scanned (either officailly adopted 16-bit UUID's or proprietary ones). This example illustrates,
how to make an API independent way of searching for BLE devices:
public BluetoothScanningFactory() {
if (isNewerAPI()) {
mScanningAdapter = new LollipopBluetoothLEScanAdapter();
} else {
mScanningAdapter = new JellyBeanBluetoothLEScanAdapter();
}
}
@Override
public void startScanning(String[] uuids) {
mScanningAdapter.startScanning(uuids);
https://riptutorial.com/ 205
}
@Override
public void stopScanning() {
mScanningAdapter.stopScanning();
}
@Override
public List<BTDevice> getFoundDeviceList() {
return mScanningAdapter.getFoundDeviceList();
}
}
API 18:
import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.os.Build;
import android.os.Parcelable;
import android.util.Log;
import bluetooth.model.BTDevice;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class JellyBeanBluetoothLEScanAdapter implements ScanningAdapter{
BluetoothAdapter bluetoothAdapter;
ScanCallback mCallback;
List<BTDevice> mBluetoothDeviceList;
public JellyBeanBluetoothLEScanAdapter() {
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mCallback = new ScanCallback();
mBluetoothDeviceList = new ArrayList<>();
}
@Override
public void startScanning(String[] uuids) {
if (uuids == null || uuids.length == 0) {
return;
}
UUID[] uuidList = createUUIDList(uuids);
bluetoothAdapter.startLeScan(uuidList, mCallback);
}
https://riptutorial.com/ 206
}
return uuidList;
}
@Override
public void stopScanning() {
bluetoothAdapter.stopLeScan(mCallback);
}
@Override
public List<BTDevice> getFoundDeviceList() {
return mBluetoothDeviceList;
}
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
if (isAlreadyAdded(device)) {
return;
}
BTDevice btDevice = new BTDevice();
btDevice.setName(new String(device.getName()));
btDevice.setAddress(device.getAddress());
mBluetoothDeviceList.add(btDevice);
Log.d("Bluetooth discovery", device.getName() + " " + device.getAddress());
Parcelable[] uuids = device.getUuids();
String uuid = "";
if (uuids != null) {
for (Parcelable ep : uuids) {
uuid += ep + " ";
}
Log.d("Bluetooth discovery", device.getName() + " " + device.getAddress() + "
" + uuid);
}
}
API 21:
import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.os.Build;
import android.os.ParcelUuid;
https://riptutorial.com/ 207
import bluetooth.model.BTDevice;
import java.util.ArrayList;
import java.util.List;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class LollipopBluetoothLEScanAdapter implements ScanningAdapter {
BluetoothLeScanner bluetoothLeScanner;
ScanCallback mCallback;
List<BTDevice> mBluetoothDeviceList;
public LollipopBluetoothLEScanAdapter() {
bluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();
mCallback = new ScanCallback();
mBluetoothDeviceList = new ArrayList<>();
}
@Override
public void startScanning(String[] uuids) {
if (uuids == null || uuids.length == 0) {
return;
}
List<ScanFilter> filterList = createScanFilterList(uuids);
ScanSettings scanSettings = createScanSettings();
bluetoothLeScanner.startScan(filterList, scanSettings, mCallback);
}
@Override
public void stopScanning() {
bluetoothLeScanner.stopScan(mCallback);
}
@Override
public List<BTDevice> getFoundDeviceList() {
return mBluetoothDeviceList;
}
@Override
public void onScanResult(int callbackType, ScanResult result) {
https://riptutorial.com/ 208
super.onScanResult(callbackType, result);
if (result == null) {
return;
}
BTDevice device = new BTDevice();
device.setAddress(result.getDevice().getAddress());
device.setName(new
StringBuffer(result.getScanRecord().getDeviceName()).toString());
if (device == null || device.getAddress() == null) {
return;
}
if (isAlreadyAdded(device)) {
return;
}
mBluetoothDeviceList.add(device);
}
scanningFactory.startScanning({uuidlist});
https://riptutorial.com/ 209
Chapter 40: Bluetooth Low Energy
Introduction
This documentation is meant as an enhancement over the original documentation and it will focus
on the latest Bluetooth LE API introduced in Android 5.0 (API 21). Both Central and Peripheral
roles will be covered as well as how to start scanning and advertising operations.
Examples
Finding BLE Devices
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
If you're targeting devices with Android 6.0 (API Level 23) or higher and want to perform
scanning/advertising operations you will require a Location permission:
android.permission.ACCESS_FINE_LOCATION
or
android.permission.ACCESS_COARSE_LOCATION
Note.- Devices with Android 6.0 (API Level 23) or higher also need to have Location
Services enabled.
The startScan (ScanCallback callback)method of the BluetoothLeScanner class is the most basic
way to start a scanning operation. A ScanCallback object is required to receive results:
bluetoothAdapter.getBluetoothLeScanner().startScan(new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
Log.i(TAG, "Remote device name: " + result.getDevice().getName());
}
});
https://riptutorial.com/ 210
Connecting to a GATT Server
Once you have discovered a desired BluetoothDevice object, you can connect to it by using its
connectGatt() method which takes as parameters a Context object, a boolean indicating whether to
automatically connect to the BLE device and a BluetoothGattCallback reference where connection
events and client operations results will be delivered:
BluetoothGattCallback bluetoothGattCallback =
new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i(TAG, "Connected to GATT server.");
Once you are connected to a Gatt Server, you're going to be interacting with it by writing and
reading from the server's characteristics. To do this, first you have to discover what services are
available on this server and which characteristics are avaiable in each service:
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i(TAG, "Connected to GATT server.");
gatt.discoverServices();
}
. . .
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
List<BluetoothGattService> services = gatt.getServices();
for (BluetoothGattService service : services) {
List<BluetoothGattCharacteristic> characteristics =
https://riptutorial.com/ 211
service.getCharacteristics();
for (BluetoothGattCharacteristic characteristic : characteristics) {
///Once you have a characteristic object, you can perform read/write
//operations with it
}
}
}
}
characteristic.setValue(newValue);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
gatt.writeCharacteristic(characteristic);
When the write process has finished, the onCharacteristicWrite method of your
BluetoothGattCallback will be called:
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic
characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.d(TAG, "Characteristic " + characteristic.getUuid() + " written);
}
gatt.readCharacteristic(characteristic);
When the write process has finished, the onCharacteristicRead method of your
BluetoothGattCallback will be called:
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic
characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
byte[] value = characteristic.getValue();
}
You can request to be notified from the Gatt Server when the value of a characteristic has been
changed:
gatt.setCharacteristicNotification(characteristic, true);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
All notifications from the server will be received in the onCharacteristicChanged method of your
BluetoothGattCallback:
https://riptutorial.com/ 212
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic
characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
byte[] newValue = characteristic.getValue();
}
You can use Bluetooth LE Advertising to broadcast data packages to all nearby devices without
having to establish a connection first. Bear in mind that there's a strict limit of 31 bytes of
advertisement data. Advertising your device is also the first step towards letting other users
connect to you.
Since not all devices support Bluetooth LE Advertising, the first step is to check that your device
has all the necessary requirements to support it. Afterwards, you can initialize a
BluetoothLeAdvertiser object and with it, you can start advertising operations:
@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
Log.e(TAG, "onStartFailure: "+errorCode );
}
};
advertising.startAdvertising(settingsBuilder.build(),dataBuilder.build(),advertiseCallback);
}
In order for your device to act as a peripheral, first you need to open a BluetoothGattServer and
https://riptutorial.com/ 213
populate it with at least one BluetoothGattService and one BluetoothGattCharacteristic:
BluetoothGattServer server=bluetoothManager.openGattServer(context,
bluetoothGattServerCallback);
characteristic.addDescriptor(new BluetoothGattDescriptor(UUID.fromString("00002902-0000-1000-
8000-00805f9b34fb"), BluetoothGattCharacteristic.PERMISSION_WRITE));
service.addCharacteristic(characteristic);
server.addService(service);
@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId,
int offset, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset,
characteristic);
}
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int
requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean
responseNeeded, int offset, byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic,
preparedWrite, responseNeeded, offset, value);
}
@Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int
offset, BluetoothGattDescriptor descriptor) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor);
}
https://riptutorial.com/ 214
@Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset,
byte[] value) {
super.onDescriptorWriteRequest(device, requestId, descriptor,
preparedWrite, responseNeeded, offset, value);
}
Whenever you receive a request for a write/read to a characteristic or descriptor you must send a
response to it in order for the request to be completed succesfully :
@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
server.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, YOUR_RESPONSE);
}
https://riptutorial.com/ 215
Chapter 41: Bottom Sheets
Introduction
A bottom sheet is a sheet that slides up from the bottom edge of the screen.
Remarks
Bottom sheets slide up from the bottom of the screen to reveal more content.
They were added to the Android Support Library in v23.2.0 version.
Examples
BottomSheetBehavior like Google maps
2.1.x
BottomSheetBehavior is characterized by :
1. Two toolbars with animations that respond to the bottom sheet movements.
2. A FAB that hides when it is near to the "modal toolbar" (the one that appears when you are
sliding up).
3. A backdrop image behind bottom sheet with some kind of parallax effect.
4. A Title (TextView) in Toolbar that appears when bottom sheet reach it.
5. The notification satus bar can turn its background to transparent or full color.
6. A custom bottom sheet behavior with an "anchor" state.
ToolBars
When you open that view in Google Maps, you can see a toolbar in where you can search, it's the
only one that I'm not doing exactly like Google Maps, because I wanted to do it more generic.
Anyway that ToolBar is inside an AppBarLayout and it got hidden when you start dragging the
BottomSheet and it appears again when the BottomSheet reach the COLLAPSED state.
To achieve it you need to:
https://riptutorial.com/ 216
This is how I did it for first toolbar or ActionBar:
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof NestedScrollView;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
View dependency) {
if (mChild == null) {
initValues(child, dependency);
return false;
}
//going up
if (dVerticalScroll <= 0 && !hidden) {
dismissAppBar(child);
return true;
}
return false;
}
mChild = child;
mInitialY = child.getY();
BottomSheetBehaviorGoogleMapsLike bottomSheetBehavior =
BottomSheetBehaviorGoogleMapsLike.from(dependency);
bottomSheetBehavior.addBottomSheetCallback(new
BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet,
@BottomSheetBehaviorGoogleMapsLike.State int newState) {
if (newState == BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED ||
newState == BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN)
showAppBar(child);
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
}
mToolbarAnimation.y(-(mChild.getHeight()+25)).start();
}
https://riptutorial.com/ 217
private void showAppBar(View child) {
hidden = false;
AppBarLayout appBarLayout = (AppBarLayout)child;
mToolbarAnimation =
appBarLayout.animate().setDuration(mContext.getResources().getInteger(android.R.integer.config_mediumAn
mToolbarAnimation.y(mInitialY).start();
}
The code for this one is a little extensive, so I will let the link
The FAB
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child,
View dependency) {
if (offset == 0)
setOffsetValue(parent);
if (dependency.getY() <=0)
return false;
return false;
}
https://riptutorial.com/ 218
Like the others, it's a custom behavior, the only "complicated" thing in this one is the little algorithm
that keeps the image anchored to the BottomSheet and avoid the image collapse like default
parallax effect:
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
View dependency) {
if (mYmultiplier == 0) {
initValues(child, dependency);
return true;
}
//going up
if (dVerticalScroll <= 0 && child.getY() <= 0) {
child.setY(0);
return true;
}
//going down
if (dVerticalScroll >= 0 && dependency.getY() <= mImageHeight)
return false;
return true;
}
2. Copy paste code from default BottomSheetBehavior file to your new one.
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset);
https://riptutorial.com/ 219
}
int constrain(int amount, int low, int high) {
return amount < low ? low : (amount > high ? high : amount);
}
https://riptutorial.com/ 220
int top;
int targetState;
if (mLastNestedScrollDy > 0) {
//top = mMinOffset;
//targetState = STATE_EXPANDED;
int currentTop = child.getTop();
if (currentTop > mAnchorPoint) {
top = mAnchorPoint;
targetState = STATE_ANCHOR_POINT;
}
else {
top = mMinOffset;
targetState = STATE_EXPANDED;
}
} else if (mHideable && shouldHide(child, getYVelocity())) {
top = mParentHeight;
targetState = STATE_HIDDEN;
} else if (mLastNestedScrollDy == 0) {
int currentTop = child.getTop();
if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) {
top = mMinOffset;
targetState = STATE_EXPANDED;
} else {
top = mMaxOffset;
targetState = STATE_COLLAPSED;
}
} else {
//top = mMaxOffset;
//targetState = STATE_COLLAPSED;
int currentTop = child.getTop();
if (currentTop > mAnchorPoint) {
top = mMaxOffset;
targetState = STATE_COLLAPSED;
}
else {
top = mAnchorPoint;
targetState = STATE_ANCHOR_POINT;
}
}
if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
setStateInternal(STATE_SETTLING);
ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState));
} else {
setStateInternal(targetState);
}
mNestedScrolled = false;
}
https://riptutorial.com/ 221
}
return;
}
V child = mViewRef.get();
if (child == null) {
return;
}
int top;
if (state == STATE_COLLAPSED) {
top = mMaxOffset;
} else if (state == STATE_ANCHOR_POINT) {
top = mAnchorPoint;
} else if (state == STATE_EXPANDED) {
top = mMinOffset;
} else if (mHideable && state == STATE_HIDDEN) {
top = mParentHeight;
} else {
throw new IllegalArgumentException("Illegal state argument: " + state);
}
setStateInternal(STATE_SETTLING);
if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
ViewCompat.postOnAnimation(child, new SettleRunnable(child, state));
}
}
Link to the whole project where you can see all the Custom Behaviors
https://riptutorial.com/ 222
]
Quick Setup
Make sure the following dependency is added to your app's build.gradle file under dependencies:
compile 'com.android.support:design:25.3.1'
Then you can use the Bottom sheet using these options:
You can achieve a Persistent Bottom Sheet attaching a BottomSheetBehavior to a child View of a
CoordinatorLayout:
<android.support.design.widget.CoordinatorLayout >
https://riptutorial.com/ 223
<!-- ..... -->
<LinearLayout
android:id="@+id/bottom_sheet"
android:elevation="4dp"
android:minHeight="120dp"
app:behavior_peekHeight="120dp"
...
app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
You can set the state of your BottomSheetBehavior using the setState() method:
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
• STATE_COLLAPSED:this collapsed state is the default and shows just a portion of the layout
along the bottom. The height can be controlled with the app:behavior_peekHeight attribute
(defaults to 0)
• STATE_EXPANDED: the fully expanded state of the bottom sheet, where either the whole bottom
sheet is visible (if its height is less than the containing CoordinatorLayout) or the entire
CoordinatorLayout is filled
If you’d like to receive callbacks of state changes, you can add a BottomSheetCallback:
mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
// React to state change
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
// React to dragging events
}
});
https://riptutorial.com/ 224
Modal bottom sheets with BottomSheetDialogFragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.my_fragment_bottom_sheet, container);
}
}
Just use:
https://riptutorial.com/ 225
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
BottomSheetDialog d = (BottomSheetDialog) dialog;
BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
Although dialog animation is slightly noticeable but does the task of opening the DialogFragment
in full screen very well.
https://riptutorial.com/ 226
Chapter 42: BottomNavigationView
Introduction
The Bottom Navigation View has been in the material design guidelines for some time, but it hasn’t
been easy for us to implement it into our apps.
Some applications have built their own solutions, whilst others have relied on third-party open-
source libraries to get the job done.
Now the design support library is seeing the addition of this bottom navigation bar, let’s take a dive
into how we can use it!
Remarks
Represents a standard bottom navigation bar for application. It is an implementation of material
design bottom navigation.
Links:
• Official Javadoc
Examples
Basic implemetation
compile 'com.android.support:design:25.1.0'
<android.support.design.widget.BottomNavigationView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/bottom_navigation_menu"/>
https://riptutorial.com/ 227
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/my_action1"
android:enabled="true"
android:icon="@drawable/my_drawable"
android:title="@string/text"
app:showAsAction="ifRoom" />
....
</menu>
case R.id.my_action1:
//Do something...
break;
//...
}
return true;//returning false disables the Navigation bar animations
}
});
Customization of BottomNavigationView
This example I will explain how to add selector for BottomNavigationView. So you can state on UI for
icons and texts.
app:itemIconTint="@drawable/bottom_navigation_view_selector"
https://riptutorial.com/ 228
app:itemTextColor="@drawable/bottom_navigation_view_selector"
selector.xml
design.xml
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:itemBackground="@color/colorPrimary"
app:itemIconTint="@drawable/nav_item_color_state"
app:itemTextColor="@drawable/nav_item_color_state"
app:menu="@menu/bottom_navigation_main" />
This example is strictly a workaround since, currently there is no way to disable a behaviour
known as ShiftMode.
https://riptutorial.com/ 229
public static void disableMenuShiftMode(BottomNavigationView view) {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
try {
Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
shiftingMode.setAccessible(true);
shiftingMode.setBoolean(menuView, false);
shiftingMode.setAccessible(false);
for (int i = 0; i < menuView.getChildCount(); i++) {
BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i);
//noinspection RestrictedApi
item.setShiftingMode(false);
// set once again checked value, so view will be updated
//noinspection RestrictedApi
item.setChecked(item.getItemData().isChecked());
}
} catch (NoSuchFieldException e) {
Log.e("BNVHelper", "Unable to get shift mode field", e);
} catch (IllegalAccessException e) {
Log.e("BNVHelper", "Unable to change value of shift mode", e);
}
}
This disables the Shifting behaviour of the menu when item count exceeds 3 nos.
USAGE
Proguard Issue : Add following line proguard configuration file as well else, this wouldn't work.
Alternatively, you can create a Class and access this method from there. See Original Reply Here
NOTE : This is a Reflection based HOTFIX, please update this once Google's support library is
updated with a direct function call.
https://riptutorial.com/ 230
Chapter 43: BroadcastReceiver
Introduction
BroadcastReceiver (receiver) is an Android component which allows you to register for system or
application events. All registered receivers for an event are notified by the Android runtime once
this event happens.
for example, a broadcast announcing that the screen has turned off, the battery is low, or a picture
was captured.
Applications can also initiate broadcasts—for example, to let other applications know that some
data has been downloaded to the device and is available for them to use.
Examples
Introduction to Broadcast receiver
A Broadcast receiver is an Android component which allows you to register for system or
application events.
A receiver can be registered via the AndroidManifest.xml file or dynamically via the
Context.registerReceiver() method.
Here I have taken an example of ACTION_BOOT_COMPLETED which is fired by the system once the
Android has completed the boot process.
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<receiver android:name="MyReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED">
</action>
</intent-filter>
</receiver>
</application>
Now device gets booted, onReceive() method will be called and then you can do your work (e.g.
https://riptutorial.com/ 231
start a service, start an alarm).
BroadcastReceiver Basics
BroadcastReceivers are used to receive broadcast Intents that are sent by the Android OS, other
apps, or within the same app.
Each Intent is created with an Intent Filter, which requires a String action. Additional information
can be configured in the Intent.
Likewise, BroadcastReceivers register to receive Intents with a particular Intent Filter. They can be
registered programmatically:
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//Your implementation goes here.
}
}, new IntentFilter("Some Action"));
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="Some Action"/>
</intent-filter>
</receiver>
To receive the Intent, set the Action to something documented by Android OS, by another app or
API, or within your own application, using sendBroadcast:
Additionally, the Intent can contain information, such as Strings, primitives, and Parcelables, that
can be viewed in onReceive.
Using LocalBroadcastManager
https://riptutorial.com/ 232
if (intent.getAction().equals("Some Action")) {
//Do something
}
}
});
//Remember to unregister the receiver when you are done with it:
manager.unregisterReceiver(receiver);
Register broadcast
https://riptutorial.com/ 233
}
Unregister broadcast
packageManager.setComponentEnabledSetting(
componentName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
packageManager.setComponentEnabledSetting(
componentName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
Example below shows how to create a BroadcastReceiver which is able to receive BOOT_COMPLETED
events. This way, you are able to start a Service or start an Activity as soon device was powered
up.
Also, you can use BOOT_COMPLETED events to restore your alarms since they are destroyed when
device is powered off.
NOTE: The user needs to have started the application at least once before you can receive the
BOOT_COMPLETED action.
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
https://riptutorial.com/ 234
package="com.test.example" >
...
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
...
<application>
...
<receiver android:name="com.test.example.MyCustomBroadcastReceiver">
<intent-filter>
<!-- REGISTER TO RECEIVE BOOT_COMPLETED EVENTS -->
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
MyCustomBroadcastReceiver.java
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action != null) {
if (action.equals(Intent.ACTION_BOOT_COMPLETED) ) {
// TO-DO: Code to handle BOOT COMPLETED EVENT
// TO-DO: I can start an service.. display a notification... start an activity
}
}
}
}
Example of a LocalBroadcastManager
"A Broadcast receiver is an Android component which allows you to register for system
or application events."
1. since the data remains inside the application process, the data cannot be leaked.
2. LocalBroadcasts are resolved faster, since the resolution of a normal broadcast happens at
runtime throughout the OS.
SenderActivity
https://riptutorial.com/ 235
Intent intent = new Intent("anEvent");
intent.putExtra("key", "This is an event");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
ReceiverActivity
1. Register a receiver
LocalBroadcastManager.getInstance(this).registerReceiver(aLBReceiver,
new IntentFilter("anEvent"));
@Override
protected void onPause() {
// Unregister since the activity is about to be closed.
LocalBroadcastManager.getInstance(this).unregisterReceiver(aLBReceiver);
super.onDestroy();
}
You can communicate two activities so that Activity A can be notified of an event happening in
Activity B.
Activity A
@Override
protected void onCreate(Bundle savedInstanceState) {
registerEventReceiver();
super.onCreate(savedInstanceState);
}
@Override
protected void onDestroy() {
unregisterEventReceiver(eventReceiver);
super.onDestroy();
}
https://riptutorial.com/ 236
}
Activity B
Of course you can add more information to the broadcast adding extras to the Intent that is passed
between the activities. Not added to keep the example as simple as possible.
Sticky Broadcast
Ordered broadcasts are used when you need to specify a priority for broadcast listeners.
In this example firstReceiver will receive broadcast always before than a secondReceiver:
https://riptutorial.com/ 237
// register our receivers
context.registerReceiver(firstReceiver, firstFilter);
context.registerReceiver(secondReceiver, secondFilter);
@Override
public void onReceive(final Context context, final Intent intent) {
abortBroadcast();
}
in this case all receivers with lower priority will not receive a broadcast message.
Starting with Android 3.1 all applications, upon installation, are placed in a stopped state. While in
stopped state, the application will not run for any reason, except by a manual launch of an activity,
or an explicit intent that addresses an activity ,service or broadcast.
When writing system app that installs APKs directly, please take into account that the newly
installed APP won't receive any broadcasts until moved into a non stopped state.
An easy way to to activate an app is to sent a explicit broadcast to this app. as most apps
implement INSTALL_REFERRER, we can use it as a hooking point
Scan the manifest of the installed app, and send an explicit broadcast to to each receiver:
https://riptutorial.com/ 238
Chapter 44: Building Backwards Compatible
Apps
Examples
How to handle deprecated API
It is unlikely for a developer to not come across a deprecated API during a development process.
A deprecated program element is one that programmers are discouraged from using, typically
because it is dangerous, or because a better alternative exists. Compilers and analyzers (like LINT
) warn when a deprecated program element is used or overridden in non-deprecated code.
A deprecated API is usually identified in Android Studio using a strikeout. In the example below,
the method .getColor(int id) is deprecated:
getResources().getColor(R.color.colorAccent));
If possible, developers are encouraged to use alternative APIs and elements. It is possible to
check backwards compatibility of a library by visiting the Android documentation for the library and
checking the "Added in API level x" section:
https://riptutorial.com/ 239
https://riptutorial.com/ 240
https://riptutorial.com/android/topic/4291/building-backwards-compatible-apps
https://riptutorial.com/ 241
Chapter 45: ButterKnife
Introduction
Butterknife is a view binding tool that uses annotations to generate boilerplate code for us. This
tool is developed by Jake Wharton at Square and is essentially used to save typing repetitive lines
of code like findViewById(R.id.view) when dealing with views thus making our code look a lot
cleaner.
To be clear, Butterknife is not a dependency injection library. Butterknife injects code at compile
time. It is very similar to the work done by Android Annotations.
Remarks
ButterKnife
Field and method binding for Android views which uses annotation processing to generate
boilerplate code for you.
License
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing permissions and
limitations under the License.
Examples
Configuring ButterKnife in your project
https://riptutorial.com/ 242
Configure your project-level build.gradle to include the android-apt plugin:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.jakewharton:butterknife-gradle-plugin:8.5.1'
}
}
Then, apply the android-apt plugin in your module-level build.gradle and add the ButterKnife
dependencies:
android {
...
}
dependencies {
compile 'com.jakewharton:butterknife:8.5.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
}
Note: If you are using the new Jack compiler with version 2.2.0 or newer you do not need the
android-apt plugin and can instead replace apt with annotationProcessor when declaring the
compiler dependency.
In order to use ButterKnife annotations you shouldn't forget about binding them in onCreate() of
your Activities or onCreateView() of your Fragments:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Binding annotations
ButterKnife.bind(this);
// ...
}
// Or
class ExampleFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(getContentView(), container, false);
// Binding annotations
ButterKnife.bind(this, view);
https://riptutorial.com/ 243
// ...
return view;
}
Below are the additional steps you'd have to take to use ButterKnife in a library project
To use ButterKnife in a library project, add the plugin to your project-level build.gradle:
buildscript {
dependencies {
classpath 'com.jakewharton:butterknife-gradle-plugin:8.5.1'
}
}
…and then apply to your module by adding these lines on the top of your library-level build.gradle:
Now make sure you use R2 instead of R inside all ButterKnife annotations.
// Listeners
@OnClick(R.id.submit)
public void submit(View view) {
// TODO submit data to server...
}
https://riptutorial.com/ 244
Binding Views using ButterKnife
we can annotate fields with @BindView and a view ID for Butter Knife to find and automatically cast
the corresponding view in our layout.
Binding Views
Binding Views in Activity
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState) {
View view = inflater.inflate(R.layout.fancy_fragment, container, false);
unbinder = ButterKnife.bind(this, view);
// TODO Use fields...
return view;
}
// in fragments or non activity bindings we need to unbind the binding when view is about to
be destroyed
@Override
public void onDestroy() {
super.onDestroy();
unbinder.unbind();
}
}
https://riptutorial.com/ 245
infer the return type and automatically performs the cast.
Binding Resources
Apart from being useful for binding views, one could also use ButterKnife to bind resources such
as those defined within strings.xml, drawables.xml, colors.xml, dimens.xml, etc.
@Override
public void onCreate(Bundle savedInstanceState) {
// ...
ButterKnife.bind(this);
}
https://riptutorial.com/ 246
//The apply method allows you to act on all the views in a list at once.
ButterKnife.apply(nameViews, DISABLE);
ButterKnife.apply(nameViews, ENABLED, false);
//We can use Action and Setter interfaces allow specifying simple behavior.
static final ButterKnife.Action<View> DISABLE = new ButterKnife.Action<View>() {
@Override public void apply(View view, int index) {
view.setEnabled(false);
}
};
static final ButterKnife.Setter<View, Boolean> ENABLED = new ButterKnife.Setter<View,
Boolean>() {
@Override public void set(View view, Boolean value, int index) {
view.setEnabled(value);
}
};
Optional Bindings
By default, both @Bind and listener bindings are required. An exception is thrown if the target view
cannot be found. But if we are not sure if a view will be there or not then we can add a @Nullable
annotation to fields or the @Optional annotation to methods to suppress this behavior and create an
optional binding.
@Nullable
@BindView(R.id.might_not_be_there) TextView mightNotBeThere;
@Optional
@OnClick(R.id.maybe_missing)
void onMaybeMissingClicked() {
// TODO ...
}
OnClick Listener:
@OnClick(R.id.login)
public void login(View view) {
// Additional logic
}
@OnClick(R.id.login)
public void login() {
// Additional logic
}
https://riptutorial.com/ 247
@OnClick(R.id.submit)
public void sayHi(Button button) {
button.setText("Hello!");
}
Custom Views can bind to their own listeners by not specifying an ID:
Fragments have a different view lifecycle than activities. When binding a fragment in
onCreateView, set the views to null in onDestroyView. Butter Knife returns an Unbinder instance
when you call bind to do this for you. Call its unbind method in the appropriate lifecycle callback.
An example:
https://riptutorial.com/ 248
Android Studio ButterKnife Plugin
Note : Make sure that you make the right click for your_xml_layou(R.layout.your_xml_layou) else
the Generate menu will not contain Butterknife injector option.
https://riptutorial.com/ 249
Chapter 46: Button
Syntax
• <Button ... />
• android:onClick="methodname"
• button.setOnClickListener(new OnClickListener(){...});
• public class classname implements View.OnLongClickListener
Examples
inline onClickListener
Say we have a button (we can create it programmatically, or bind it from a view using
findViewbyId(), etc...)
btnOk.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Do stuff here...
}
});
When we create a button in layout, we can use the android:onClick attribute to reference a method
in code to handle clicks.
Button
<Button
android:width="120dp"
android:height="wrap_content"
android:text="Click me"
android:onClick="handleClick" />
https://riptutorial.com/ 250
Using the same click event for one or more Views in the XML
When we create any View in layout, we can use the android:onClick attribute to reference a
method in the associated activity or fragment to handle the click events.
XML Layout
<Button android:id="@+id/button"
...
// onClick should reference the method in your activity or fragment
android:onClick="doSomething" />
// Note that this works with any class which is a subclass of View, not just Button
<ImageView android:id="@+id/image"
...
android:onClick="doSomething" />
Activity/fragment code
In your code, create the method you named, where v will be the view that was touched, and do
something for each view that calls this method.
If you want, you can also use different method for each View (in this case, of course, you don't
have to check for the ID).
To catch a long click and use it you need to provide appropriate listener to button:
button.setOnLongClickListener(listener);
https://riptutorial.com/ 251
When should I use it
• When the code inside an inline listener is too big and your method / class becomes ugly and
hard to read
• You want to perform same action in various elements (view) of your app
To achieve this you need to create a class implementing one of the listeners in the View API.
@Override
public void onLongClick(View v) {
// show help toast or popup
}
}
Then you just need to have an attribute or variable in your Activity to use it:
button1.setOnClickListener(helpListener);
button2.setOnClickListener(helpListener);
label.setOnClickListener(helpListener);
button1.setOnClickListener(helpListener);
NOTE: defining listeners in separated class has one disadvantage, it cannot access class fields
directly, so you need to pass data (context, view) through constructor unless you make attributes
public or define geters.
In order to prevent a button from firing multiple times within a short period of time (let's say
2 clicks within 1 second, which may cause serious problems if the flow is not controlled), one can
implement a custom SingleClickListener.
This ClickListener sets a specific time interval as threshold (for instance, 1000ms).
When the button is clicked, a check will be ran to see if the trigger was executed in the past
amount of time you defined, and if not it will trigger it.
public SingleClickListener() {
https://riptutorial.com/ 252
this(1000);
}
@Override
public void onClick(View v) {
if (SystemClock.elapsedRealtime() - lastTimeClicked < defaultInterval) {
return;
}
lastTimeClicked = SystemClock.elapsedRealtime();
performClick(v);
}
There are many possible ways of customizing the look of a Button. This example presents several
options:
styles.xml
<resources>
<style name=“mybutton” parent=”ThemeOverlay.AppCompat.Ligth”>
<!-- customize colorButtonNormal for the disable color -->
<item name="colorButtonNormal">@color/colorbuttonnormal</item>
<!-- customize colorAccent for the enabled color -->
<item name="colorButtonNormal">@color/coloraccent</item>
</style>
</resources>
Then in the layout where you place your button (e.g. MainActivity):
activity_main.xml
https://riptutorial.com/ 253
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<Button
android:id="@+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello"
android:theme="@style/mybutton"
style="@style/Widget.AppCompat.Button.Colored"/>
</LinearLayout>
styles.xml
<resources>
<style name="mybuttonstyle" parent="@android:style/Widget.Button">
<item name="android:gravity">center_vertical|center_horizontal</item>
<item name="android:textColor">#FFFFFFFF</item>
<item name="android:shadowColor">#FF000000</item>
<item name="android:shadowDx">0</item>
<item name="android:shadowDy">-1</item>
<item name="android:shadowRadius">0.2</item>
<item name="android:textSize">16dip</item>
<item name="android:textStyle">bold</item>
<item name="android:background">@drawable/button</item>
</style>
</resources>
Then in the layout where you place your button (e.g. in MainActivity):
activity_main.xml
https://riptutorial.com/ 254
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<Button
android:id="@+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello"
android:theme="@style/mybuttonstyle"/>
</LinearLayout>
Create an xml file into drawable folder called 'mybuttondrawable.xml' to define the drawable
resource of each of your button states:
drawable/mybutton.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_enabled="false"
android:drawable="@drawable/mybutton_disabled" />
<item
android:state_pressed="true"
android:state_enabled="true"
android:drawable="@drawable/mybutton_pressed" />
<item
android:state_focused="true"
android:state_enabled="true"
android:drawable="@drawable/mybutton_focused" />
<item
android:state_enabled="true"
android:drawable="@drawable/mybutton_enabled" />
</selector>
Each of those drawables may be images (e.g. mybutton_disabled.png) or xml files defined by you
and stored in the drawables folder. For instance:
drawable/mybutton_disabled.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#F2F2F2"
android:centerColor="#A4A4A4"
android:endColor="#F2F2F2"
android:angle="90"/>
<padding android:left="7dp"
android:top="7dp"
android:right="7dp"
android:bottom="7dp" />
https://riptutorial.com/ 255
<stroke
android:width="2dip"
android:color="#FFFFFF" />
<corners android:radius= "8dp" />
</shape>
Then in the layout where you place your button (e.g. MainActivity):
activity_main.xml
<Button
android:id="@+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello"
android:background="@drawable/mybuttondrawable"/>
</LinearLayout>
You can override the default android button style in the definition of your app theme (in
values/styles.xml).
styles.xml
<resources>
<style name="AppTheme" parent="android:Theme">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:button">@style/mybutton</item>
</style>
https://riptutorial.com/ 256
<item name="android:textStyle">bold</item>
<item name="android:background">@drawable/anydrawable</item>
</style>
</resources>
Just find you button in your activity and apply a color filter:
You can check different blending modes here and nice examples here.
https://riptutorial.com/ 257
Chapter 47: Callback URL
Examples
Callback URL example with Instagram OAuth
One of the use cases of callback URLs is OAuth. Let us do this with an Instagram Login: If the
user enters their credentials and clicks the Login button, Instagram will validate the credentials and
return an access_token. We need that access_token in our app.
For our app to be able to listen to such links, we need to add a callback URL to our Activity. We
can do this by adding an <intent-filter/> to our Activity, which will react to that callback URL.
Assume that our callback URL is appSchema://appName.com. Then you have to add the following lines
to your desired Activity in the Manifest.xml file:
Now, in order to get the contents of the URL in your Activity, you need to override the onResume()
method as follows:
@Override
public void onResume() {
// The following line will return "appSchema://appName.com".
String CALLBACK_URL = getResources().getString(R.string.insta_callback);
Uri uri = getIntent().getData();
if (uri != null && uri.toString().startsWith(CALLBACK_URL)) {
String access_token = uri.getQueryParameter("access_token");
}
// Perform other operations here.
}
Now you have retrieved the access_token from Instagram, that is used in various API endpoints of
Instagram.
https://riptutorial.com/ 258
Chapter 48: Camera 2 API
Parameters
Parameter Details
The subset of the results of a single image capture from the image
sensor. Contains a subset of the final configuration for the capture
CaptureResult hardware (sensor, lens, flash), the processing pipeline, the control
algorithms, and the output buffers. It is produced by a CameraDevice
after processing a CaptureRequest
Remarks
• Camera2 APIs are available in API 21+ (Lollipop and beyond)
• Even if an Android device has a 21+ ROM officially, there is no guarantee that it implements
Camera2 APIs, it's totally up to the manufacturer to implement it or not (Example: LG G2 has
official Lollipop support, but no Camera2 APIs)
• With Camera2, Camera ("Camera1") is deprecated
• With great power comes great responsability: It's easier to mess it up when using this APIs.
• Remember, if you only want to take a photo in your app, and simply get it, you don't need to
implement Camera2, you can open the device's camera app via an Intent, and receive it
https://riptutorial.com/ 259
back
Examples
Preview the main camera in a TextureView
In this case, building against API 23, so permissions are handled too.
You must add in the Manifest the following permission (wherever the API level you're using):
<uses-permission android:name="android.permission.CAMERA"/>
We're about to create an activity (Camera2Activity.java) that fills a TextureView with the preview of
the device's camera.
Attributes (You may need to read the entire example to understand some of it)
@Override
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
}
https://riptutorial.com/ 260
};
A CameraDevice represent one physical device's camera. In this attribute, we save the ID of the
current CameraDevice
This is the view (TextureView) that we'll be using to "draw" the preview of the Camera
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
// This method is called when the camera is opened. We start camera preview here.
mCameraOpenCloseLock.release();
mCameraDevice = cameraDevice;
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int error) {
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
finish();
}
};
https://riptutorial.com/ 261
An additional thread for running tasks that shouldn't block the UI
A Semaphore to prevent the app from exiting before closing the camera.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera2);
@Override
public void onResume() {
super.onResume();
startBackgroundThread();
// When the screen is turned off and turned back on, the SurfaceTexture is already
// available, and "onSurfaceTextureAvailable" will not be called. In that case, we can
open
// a camera and start preview from here (otherwise, we wait until the surface is ready in
// the SurfaceTextureListener).
if (mTextureView.isAvailable()) {
openCamera(mTextureView.getWidth(), mTextureView.getHeight());
} else {
https://riptutorial.com/ 262
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
}
@Override
public void onPause() {
closeCamera();
stopBackgroundThread();
super.onPause();
}
https://riptutorial.com/ 263
}
// Danger! Attempting to use too large a preview size could exceed the camera
// bus' bandwidth limitation, resulting in gorgeous previews but the storage of
// garbage capture data.
mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
maxPreviewHeight, largest);
mCameraId = cameraId;
return;
}
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (NullPointerException e) {
https://riptutorial.com/ 264
// Currently an NPE is thrown when the Camera2API is used but not supported on the
// device this code runs.
Toast.makeText(Camera2Activity.this, "Camera2 API not supported on this device",
Toast.LENGTH_LONG).show();
}
}
// We configure the size of default buffer to be the size of camera preview we want.
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
@Override
public void onConfigured(@NonNull CameraCaptureSession
cameraCaptureSession) {
// The camera is already closed
if (null == mCameraDevice) {
return;
}
@Override
public void onConfigureFailed(
@NonNull CameraCaptureSession cameraCaptureSession) {
showToast("Failed");
}
}, null
);
https://riptutorial.com/ 265
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}
})
.create();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA_PERMISSION);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED)
{
Toast.makeText(Camera2Activity.this, "ERROR: Camera permissions not granted",
Toast.LENGTH_LONG).show();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
https://riptutorial.com/ 266
private void stopBackgroundThread() {
mBackgroundThread.quitSafely();
try {
mBackgroundThread.join();
mBackgroundThread = null;
mBackgroundHandler = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Utility methods
Given choices of Sizes supported by a camera, choose the smallest one that is at least at large as
the respective texture view size, and that is as most as large as the respective max size, and
whose aspect ratio matches with the specified value. If doesn't exist, choose the largest one that is
at most as large as the respective max size, and whose aspect ratio matches with the specified
value
// Collect the supported resolutions that are at least as big as the preview Surface
List<Size> bigEnough = new ArrayList<>();
// Collect the supported resolutions that are smaller than the preview Surface
List<Size> notBigEnough = new ArrayList<>();
int w = aspectRatio.getWidth();
int h = aspectRatio.getHeight();
for (Size option : choices) {
if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
option.getHeight() == option.getWidth() * h / w) {
if (option.getWidth() >= textureViewWidth &&
option.getHeight() >= textureViewHeight) {
bigEnough.add(option);
} else {
notBigEnough.add(option);
}
}
}
// Pick the smallest of those big enough. If there is no one big enough, pick the
// largest of those not big enough.
if (bigEnough.size() > 0) {
return Collections.min(bigEnough, new CompareSizesByArea());
} else if (notBigEnough.size() > 0) {
return Collections.max(notBigEnough, new CompareSizesByArea());
} else {
Log.e("Camera2", "Couldn't find any suitable preview size");
return choices[0];
}
}
https://riptutorial.com/ 267
return;
}
int rotation = getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale = Math.max(
(float) viewHeight / mPreviewSize.getHeight(),
(float) viewWidth / mPreviewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
mTextureView.setTransform(matrix);
}
@Override
public int compare(Size lhs, Size rhs) {
// We cast here to ensure the multiplications won't overflow
return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
(long) rhs.getWidth() * rhs.getHeight());
}
}
/**
* Shows a {@link Toast} on the UI thread.
*
* @param text The message to show
*/
private void showToast(final String text) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(Camera2Activity.this, text, Toast.LENGTH_SHORT).show();
}
});
}
https://riptutorial.com/ 268
Chapter 49: Camera and Gallery
Examples
Taking full-sized photo from camera
• - to open camera app. If attribute required is set to true you will not be able to install
Camera
this app if you don't have hardware camera.
• WRITE_EXTERNAL_STORAGE - This permission is required to create new file, in which captured
photo will be saved.
AndroidManifest.xml
<uses-feature android:name="android.hardware.camera"
android:required="true" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
The main idea in taking full-sized photo from camera is that we need to create new file for photo,
before we open camera app and capture photo.
https://riptutorial.com/ 269
storageDir /* directory */
);
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
if (!storageDir.mkdirs()) {
if (!storageDir.exists()) {
Log.d("CameraSample", "failed to create directory");
return null;
}
}
} else {
Log.v(getString(R.string.app_name), "External storage is not mounted READ/WRITE.");
}
return storageDir;
}
/* There isn't enough memory to open up more than a couple camera photos */
/* So pre-scale the target bitmap into which the file is decoded */
https://riptutorial.com/ 270
/* Decode the JPEG file into a Bitmap */
Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix,
false);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return 90f;
case ExifInterface.ORIENTATION_ROTATE_180:
return 180f;
case ExifInterface.ORIENTATION_ROTATE_270:
return 270f;
default:
return 0f;
}
} catch (Exception e) {
Log.e("Add Recipe", "getRotation", e);
return 0f;
}
}
if (mCurrentPhotoPath != null) {
setPic();
galleryAddPic();
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) {
handleBigCameraPhoto();
}
}
Take photo
https://riptutorial.com/ 271
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Xml file :
Activity
import java.io.IOException;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.ImageView;
//Camera variables
//a surface holder
private SurfaceHolder sHolder;
//a variable to control the camera
private Camera mCamera;
//the camera parameters
private Parameters parameters;
https://riptutorial.com/ 272
//get the Surface View at the main.xml file
sv = (SurfaceView) findViewById(R.id.surfaceView);
//Get a surface
sHolder = sv.getHolder();
//add the callback interface methods defined below as the Surface View callbacks
sHolder.addCallback(this);
//tells Android that this surface will have its data constantly replaced
sHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3)
{
//get camera parameters
parameters = mCamera.getParameters();
@Override
public void surfaceCreated(SurfaceHolder holder)
{
https://riptutorial.com/ 273
// The Surface has been created, acquire the camera and tell it where
// to draw the preview.
mCamera = Camera.open();
try {
mCamera.setPreviewDisplay(holder);
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
//stop the preview
mCamera.stopPreview();
//release the camera
mCamera.release();
//unbind the camera from this object
mCamera = null;
}
}
First of all you need Uri and temp Folders and request codes :
https://riptutorial.com/ 274
public void openCamera(){
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
try {
mImageCaptureUri = null;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
mImageCaptureUri = Uri.fromFile(mFileTemp);
} else {
mImageCaptureUri = InternalStorageContentProvider.CONTENT_URI;
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageCaptureUri);
intent.putExtra("return-data", true);
startActivityForResult(intent, REQUEST_CODE_TAKE_PICTURE);
} catch (Exception e) {
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
}
Bitmap bitmap;
switch (requestCode) {
case REQUEST_SELECT_PICTURE:
try {
Uri uri = data.getData();
try {
bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
Bitmap bitmapScaled = Bitmap.createScaledBitmap(bitmap, 800, 800, true);
Drawable drawable=new BitmapDrawable(bitmapScaled);
https://riptutorial.com/ 275
mImage.setImageDrawable(drawable);
mImage.setVisibility(View.VISIBLE);
} catch (IOException e) {
Log.v("act result", "there is an error : "+e.getContent());
}
} catch (Exception e) {
Log.v("act result", "there is an error : "+e.getContent());
}
break;
case REQUEST_CODE_TAKE_PICTURE:
try{
Bitmap bitmappicture = MediaStore.Images.Media.getBitmap(getContentResolver() ,
mImageCaptureUri);
mImage.setImageBitmap(bitmappicture);
mImage.setVisibility(View.VISIBLE);
}catch (IOException e){
Log.v("error camera",e.getMessage());
}
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
And you need to handle runtime permissions such as Read/Write external storage etc ...
My requestPermission method :
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
switch (requestCode) {
https://riptutorial.com/ 276
case REQUEST_STORAGE_READ_ACCESS_PERMISSION:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
handleGallery();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
showAlertDialog method :
Decode bitmap correctly rotated from the uri fetched with the intent
https://riptutorial.com/ 277
@Nullable
public Bitmap getBitmap(@NonNull Uri bitmapUri, int maxDimen) {
InputStream is = context.getContentResolver().openInputStream(bitmapUri);
Bitmap bitmap = BitmapFactory.decodeStream(is, null, getBitmapOptions(bitmapUri,
maxDimen));
return bitmap;
}
try {
boolean hasRotation = false;
//If image comes from the gallery and is not in the folder DCIM (Scheme: content://)
String[] projection = {MediaStore.Images.ImageColumns.ORIENTATION};
Cursor cursor = context.getContentResolver().query(imgUri, projection, null, null,
null);
https://riptutorial.com/ 278
if (cursor != null) {
if (cursor.getColumnCount() > 0 && cursor.moveToFirst()) {
photoRotation = cursor.getInt(cursor.getColumnIndex(projection[0]));
hasRotation = photoRotation != 0;
Log.d("Cursor orientation: "+ photoRotation);
}
cursor.close();
}
//If image comes from the camera (Scheme: file://) or is from the folder DCIM (Scheme:
content://)
if (!hasRotation) {
ExifInterface exif = new ExifInterface(getAbsolutePath(imgUri));
int exifRotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (exifRotation) {
case ExifInterface.ORIENTATION_ROTATE_90: {
photoRotation = 90;
break;
}
case ExifInterface.ORIENTATION_ROTATE_180: {
photoRotation = 180;
break;
}
case ExifInterface.ORIENTATION_ROTATE_270: {
photoRotation = 270;
break;
}
}
Log.d(TAG, "Exif orientation: "+ photoRotation);
}
} catch (IOException e) {
Log.e(TAG, "Error determining rotation for image"+ imgUri, e);
}
return photoRotation;
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private String getAbsolutePath(Uri uri) {
//Code snippet edited from: http://stackoverflow.com/a/20559418/2235133
String filePath = uri.getPath();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT &&
DocumentsContract.isDocumentUri(context, uri)) {
// Will return "image:x*"
String[] wholeID = TextUtils.split(DocumentsContract.getDocumentId(uri),
COLON_SEPARATOR);
// Split at colon, use second item in the array
String type = wholeID[0];
if (IMAGE.equalsIgnoreCase(type)) {//If it not type image, it means it comes from a
remote location, like Google Photos
String id = wholeID[1];
String[] column = {MediaStore.Images.Media.DATA};
// where id is equal to
String sel = MediaStore.Images.Media._ID + "=?";
Cursor cursor = context.getContentResolver().
query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
column, sel, new String[]{id}, null);
if (cursor != null) {
int columnIndex = cursor.getColumnIndex(column[0]);
if (cursor.moveToFirst()) {
filePath = cursor.getString(columnIndex);
https://riptutorial.com/ 279
}
cursor.close();
}
Log.d(TAG, "Fetched absolute path for uri" + uri);
}
}
return filePath;
}
https://riptutorial.com/ 280
Chapter 50: Canvas drawing using
SurfaceView
Remarks
It's important to understand the basic concept of the surface view before using:
Deadlocks can easily occur if the lockCanvas() and unlockCanvasAndPost() methods are not called in
the correct order.
Examples
SurfaceView with drawing thread
This example describes how to create a SurfaceView with a dedicated drawing thread. This
implementation also handles edge cases such as manufacture specific issues as well as
starting/stopping the thread to save cpu time.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
/**
* Defines a custom SurfaceView class which handles the drawing thread
**/
public class BaseSurface extends SurfaceView implements SurfaceHolder.Callback,
View.OnTouchListener, Runnable
{
/**
* Holds the surface frame
*/
private SurfaceHolder holder;
/**
* Draw thread
*/
https://riptutorial.com/ 281
private Thread drawThread;
/**
* True when the surface is ready to draw
*/
private boolean surfaceReady = false;
/**
* Drawing thread flag
*/
/**
* Paint for drawing the sample rectangle
*/
private Paint samplePaint = new Paint();
/**
* Time per frame for 60 FPS
*/
private static final int MAX_FRAME_TIME = (int) (1000.0 / 60.0);
// red
samplePaint.setColor(0xffff0000);
// smooth edges
samplePaint.setAntiAlias(true);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
if (width == 0 || height == 0)
{
return;
}
// resize your UI
}
@Override
public void surfaceCreated(SurfaceHolder holder)
{
this.holder = holder;
if (drawThread != null)
{
Log.d(LOGTAG, "draw thread still active..");
drawingActive = false;
try
{
https://riptutorial.com/ 282
drawThread.join();
} catch (InterruptedException e)
{ // do nothing
}
}
surfaceReady = true;
startDrawThread();
Log.d(LOGTAG, "Created");
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
// Surface is not used anymore - stop the drawing thread
stopDrawThread();
// and release the surface
holder.getSurface().release();
this.holder = null;
surfaceReady = false;
Log.d(LOGTAG, "Destroyed");
}
@Override
public boolean onTouch(View v, MotionEvent event)
{
// Handle touch events
return true;
}
/**
* Stops the drawing thread
*/
public void stopDrawThread()
{
if (drawThread == null)
{
Log.d(LOGTAG, "DrawThread is null");
return;
}
drawingActive = false;
while (true)
{
try
{
Log.d(LOGTAG, "Request last frame");
drawThread.join(5000);
break;
} catch (Exception e)
{
Log.e(LOGTAG, "Could not join with draw thread");
}
}
drawThread = null;
}
/**
* Creates a new draw thread and starts it.
*/
public void startDrawThread()
https://riptutorial.com/ 283
{
if (surfaceReady && drawThread == null)
{
drawThread = new Thread(this, "Draw thread");
drawingActive = true;
drawThread.start();
}
}
@Override
public void run()
{
Log.d(LOGTAG, "Draw thread started");
long frameStartTime;
long frameTime;
/*
* In order to work reliable on Nexus 7, we place ~500ms delay at the start of drawing
thread
* (AOSP - Issue 58385)
*/
if (android.os.Build.BRAND.equalsIgnoreCase("google") &&
android.os.Build.MANUFACTURER.equalsIgnoreCase("asus") &&
android.os.Build.MODEL.equalsIgnoreCase("Nexus 7"))
{
Log.w(LOGTAG, "Sleep 500ms (Device: Asus Nexus 7)");
try
{
Thread.sleep(500);
} catch (InterruptedException ignored)
{
}
}
try
{
while (drawingActive)
{
if (holder == null)
{
return;
}
frameStartTime = System.nanoTime();
Canvas canvas = holder.lockCanvas();
if (canvas != null)
{
// clear the screen using black
canvas.drawARGB(255, 0, 0, 0);
try
{
// Your drawing here
canvas.drawRect(0, 0, getWidth() / 2, getHeight() / 2, samplePaint);
} finally
{
holder.unlockCanvasAndPost(canvas);
}
}
https://riptutorial.com/ 284
frameTime = (System.nanoTime() - frameStartTime) / 1000000;
if (frameTime < MAX_FRAME_TIME) // faster than the max fps - limit the FPS
{
try
{
Thread.sleep(MAX_FRAME_TIME - frameTime);
} catch (InterruptedException e)
{
// ignore
}
}
}
} catch (Exception e)
{
Log.w(LOGTAG, "Exception while locking/unlocking");
}
Log.d(LOGTAG, "Draw thread finished");
}
}
This layout only contains the custom SurfaceView and maximizes it to the screen size.
<sample.devcore.org.surfaceviewsample.BaseSurface
android:id="@+id/baseSurface"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
The activity which uses the SurfaceView is responsible for starting and stopping the drawing
thread. This approach saves battery as the drawing is stopped as soon as the activity gets in the
background.
import android.app.Activity;
import android.os.Bundle;
/**
* Surface object
*/
private BaseSurface surface;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surface = (BaseSurface) findViewById(R.id.baseSurface);
https://riptutorial.com/ 285
}
@Override
protected void onResume()
{
super.onResume();
// start the drawing
surface.startDrawThread();
}
@Override
protected void onPause()
{
// stop the drawing to save cpu time
surface.stopDrawThread();
super.onPause();
}
}
https://riptutorial.com/ 286
Chapter 51: Capturing Screenshots
Examples
Capturing Screenshot via Android Studio
https://riptutorial.com/ 287
Capturing Screenshot via ADB
If you use Linux (or Windows with Cygwin), you can run:
If you want to take a screenshot of a particular View v, then you can use the following code:
if(backgroundDrawable != null){
// Draw the background onto the canvas.
backgroundDrawable.draw(viewCanvas);
https://riptutorial.com/ 288
}
else{
viewCanvas.drawColor(Color.GREEN);
// Draw the view onto the canvas.
v.draw(viewCanvas)
}
https://riptutorial.com/ 289
Chapter 52: CardView
Introduction
A FrameLayout with a rounded corner background and shadow.
CardView uses elevation property on Lollipop for shadows and falls back to a custom emulated
shadow implementation on older platforms.
Due to expensive nature of rounded corner clipping, on platforms before Lollipop, CardView does
not clip its children that intersect with rounded corners. Instead, it adds padding to avoid such
intersection (See setPreventCornerOverlap(boolean) to change this behavior).
Parameters
Parameter Details
Inner padding between the left edge of the Card and children of
contentPaddingLeft
the CardView.
Inner padding between the right edge of the Card and children
cardElevation
of the CardView.
contentPaddingTop Inner padding between the top edge of the Card and children of
https://riptutorial.com/ 290
Parameter Details
the CardView.
Remarks
CardViewuses real elevation and dynamic shadows on Lollipop (API 21) and above. However,
before Lollipop CardView falls back to a programmatic shadow implementation.
If trying to make an ImageView fit within the rounded corners of a CardView, you may notice it does
not look correct pre-Lollipop (API 21). To fix this you should call setPreventCornerOverlap(false) on
your CardView, or add app:cardPreventCornerOverlap="false" to your layout.
Before using the CardView you have to add the support library dependency in the build.gradle file:
dependencies{
compile 'com.android.support:cardview-v7:25.2.0'
}
Official Documentation:
https://developer.android.com/reference/android/support/v7/widget/CardView.html
https://developer.android.com/training/material/lists-cards.html
Examples
Getting Started with CardView
CardView is a member of the Android Support Library, and provides a layout for cards.
To add CardView to your project, add the following line to your build.gradle dependencies.
compile 'com.android.support:cardview-v7:25.1.1'
In your layout you can then add the following to get a card.
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.CardView>
https://riptutorial.com/ 291
You can then add other layouts inside this and they will be encompassed in a card.
Also, CardView can be populated with any UI element and manipulated from code.
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp" >
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:id="@+id/item_image"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginRight="16dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/item_title"
android:layout_toRightOf="@+id/item_image"
android:layout_alignParentTop="true"
android:textSize="30sp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/item_detail"
android:layout_toRightOf="@+id/item_image"
android:layout_below="@+id/item_title"
/>
</RelativeLayout>
</android.support.v7.widget.CardView>
CardView provides a default elevation and corner radius so that cards have a consistent
appearance across the platforms.
You can customize these default values using these attributes in the xml file:
https://riptutorial.com/ 292
1. card_view:cardElevation attribute add elevation in CardView.
2. card_view:cardBackgroundColor attribute is used to customize background color of CardView's
background(you can give any color).
3. card_view:cardCornerRadius attribute is used to curve 4 edges of CardView
4. card_view:contentPadding attribute add padding between card and children of card
Here an example:
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="4dp"
card_view:cardBackgroundColor="@android:color/white"
card_view:cardCornerRadius="8dp"
card_view:contentPadding="16dp">
</android.support.v7.widget.CardView>
card.setCardBackgroundColor(....);
card.setCardElevation(...);
card.setRadius(....);
card.setContentPadding();
<android.support.v7.widget.CardView
...
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground">
...
</android.support.v7.widget.CardView>
While using Image/Colour as an background in a CardView, You might end up with slight white
paddings (If default Card colour is white) on the edges. This occurs due to the default rounded
corners in the Card View. Here is how to avoid those margins in Pre-lollipop devices.
https://riptutorial.com/ 293
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
card_view:cardPreventCornerOverlap="false"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/row_wallet_redeem_img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:src="@drawable/bg_image" />
</android.support.v7.widget.CardView>
Doing so removes an unwanted padding on the Card's edges. Here are some visual examples
related to this implementation.
2 Card with image background in API 19 without attribute (notice the paddings around image)
https://riptutorial.com/ 294
3 FIXED Card with image background in API 19 with attribute
cardView.setPreventCornerOverlap(false) (Issue now fixed)
https://riptutorial.com/ 295
Read CardView online: https://riptutorial.com/android/topic/726/cardview
https://riptutorial.com/ 296
Chapter 53: Check Data Connection
Examples
Check data connection
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo () != null && cm.getActiveNetworkInfo
().isConnectedOrConnecting ();
When your device connects to a network, an intent is sent. Many apps don’t check for these
intents, but to make your application work properly, you can listen to network change intents that
will tell you when communication is possible. To check for network connectivity you can, for
example, use the following clause:
if
(intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)){
NetworkInfo info =
intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
//perform your action when connected to a network
}
https://riptutorial.com/ 297
Chapter 54: Check Internet Connectivity
Introduction
This method is used to check weather WI-Fi is connected or not.
Syntax
• isNetworkAvailable() : To check if Internet available on device
Parameters
Parameter Detail
Remarks
If internet connected then method will return true or false.
Examples
Check if device has internet connectivity
/**
* If network connectivity is available, will return true
*
* @param context the current context
* @return boolean true if a network connection is available
*/
public static boolean isNetworkAvailable(Context context) {
ConnectivityManager connectivity = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity == null) {
Log.d("NetworkCheck", "isNetworkAvailable: No");
return false;
}
// get network info for all of the data interfaces (e.g. WiFi, 3G, LTE, etc.)
NetworkInfo[] info = connectivity.getAllNetworkInfo();
https://riptutorial.com/ 298
// make sure that there is at least one interface to test against
if (info != null) {
// iterate through the interfaces
for (int i = 0; i < info.length; i++) {
// check this interface for a connected state
if (info[i].getState() == NetworkInfo.State.CONNECTED) {
Log.d("NetworkCheck", "isNetworkAvailable: Yes");
return true;
}
}
}
return false;
}
ConnectivityManager cm = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo Info = cm.getActiveNetworkInfo();
if (Info == null || !Info.isConnectedOrConnecting()) {
Log.i(TAG, "No connection");
} else {
int netType = Info.getType();
int netSubtype = Info.getSubtype();
if (netType == ConnectivityManager.TYPE_WIFI) {
Log.i(TAG, "Wifi connection");
WifiManager wifiManager = (WifiManager)
getApplication().getSystemService(Context.WIFI_SERVICE);
List<ScanResult> scanResult = wifiManager.getScanResults();
for (int i = 0; i < scanResult.size(); i++) {
Log.d("scanResult", "Speed of wifi"+scanResult.get(i).level);//The db
level of signal
}
if (netType == ConnectivityManager.TYPE_WIFI) {
Log.i(TAG, "Wifi connection");
https://riptutorial.com/ 299
WifiManager wifiManager = (WifiManager)
getApplication().getSystemService(Context.WIFI_SERVICE);
List<ScanResult> scanResult = wifiManager.getScanResults();
for (int i = 0; i < scanResult.size(); i++) {
Log.d("scanResult", "Speed of wifi"+scanResult.get(i).level);//The db level of
signal
}
/**
* Check if there is any connectivity
*
* @param context
* @return
*/
public static boolean isConnected(Context context) {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
return (info != null && info.isConnected());
}
/**
* Check if there is fast connectivity
*
* @param context
* @return
*/
public static String isConnectedFast(Context context) {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
https://riptutorial.com/ 300
/**
* Check if the connection is fast
*
* @param type
* @param subType
* @return
*/
public static String isConnectionFast(int type, int subType) {
if (type == ConnectivityManager.TYPE_WIFI) {
System.out.println("CONNECTED VIA WIFI");
return "CONNECTED VIA WIFI";
} else if (type == ConnectivityManager.TYPE_MOBILE) {
switch (subType) {
case TelephonyManager.NETWORK_TYPE_1xRTT:
return "NETWORK TYPE 1xRTT"; // ~ 50-100 kbps
case TelephonyManager.NETWORK_TYPE_CDMA:
return "NETWORK TYPE CDMA (3G) Speed: 2 Mbps"; // ~ 14-64 kbps
case TelephonyManager.NETWORK_TYPE_EDGE:
https://riptutorial.com/ 301
} else {
return "";
}
}
https://riptutorial.com/ 302
Chapter 55: CleverTap
Introduction
Quick hacks for the analytics and engagement SDK provided by CleverTap - Android
Remarks
Get your CleverTap credentials from https://clevertap.com.
Examples
Get an instance of the SDK to record events
CleverTapAPI cleverTap;
try {
cleverTap = CleverTapAPI.getInstance(getApplicationContext());
} catch (CleverTapMetaDataNotFoundException e) {
// thrown if you haven't specified your CleverTap Account ID or Token in your
AndroidManifest.xml
} catch (CleverTapPermissionsNotSatisfied e) {
// thrown if you haven’t requested the required permissions in your AndroidManifest.xml
}
In your custom application class, override the onCreate() method, add the line below:
CleverTapAPI.setDebugLevel(1);
https://riptutorial.com/ 303
Chapter 56: Colors
Examples
Color Manipulation
To manipulate colors we will modify the argb (Alpha, Red, Green and Blue) values of a color.
Now you can reduce or increase red, green, and blue values and combine them to be a color
again:
Or if you want to add some alpha to it, you can add it while creating the color:
https://riptutorial.com/ 304
Chapter 57: ConstraintLayout
Introduction
ConstraintLayoutis a ViewGroup which allows you to position and size widgets in a flexible way. It is
compatible with Android 2.3 (API level 9) and higher.
It allows you to create large and complex layouts with a flat view hierarchy. It is similar to
RelativeLayout in that all views are laid out according to relationships between sibling views and
the parent layout, but it's more flexible than RelativeLayout and easier to use with Android Studio's
Layout Editor.
Syntax
• ConstraintLayout
○ protected ViewGroup.LayoutParams
generateLayoutParams(ViewGroup.LayoutParams params)
○ protected void onLayout(boolean changed, int left, int top, int right, int bottom)
• ConstraintLayout.LayoutParams
Parameters
https://riptutorial.com/ 305
Parameter Details
layoutDirection -
a -
widthAttr -
heightAttr -
Remarks
At Google IO 2016 Google announced a new Android layout named ConstraintLayout.
Pay attention because currently, this layout is a Beta release.
Examples
Adding ConstraintLayout to your project
To work with ConstraintLayout, you need Android Studio Version 2.2 or newer and have at least
https://riptutorial.com/ 306
version 32 (or higher) of Android Support Repository.
dependencies {
compile 'com.android.support.constraint:constraint-layout:1.0.2'
}
2. Sync project
1. Right-click on your module's layout directory, then click New > XML > Layout XML.
2. Enter a name for the layout and enter "android.support.constraint.ConstraintLayout" for the
Root Tag.
3. Click Finish.
</android.support.constraint.ConstraintLayout>
Chains
Since ConstraintLayout alpha 9, Chains are available. A Chain is a set of views inside a
ConstraintLayout that are connected in a bi-directional way between them, i.e A connected to B
with a constraint, and B connected to A with another constraint.
Example:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- this view is linked to the topTextView at the same time -->
<TextView
https://riptutorial.com/ 307
android:id="@+id/bottomTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bottom\nMkay"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/topTextView"/>
</android.support.constraint.ConstraintLayout>
In this example, the two views are positioned one under another and both of them are centered
vertically. You may change the vertical position of these views by adjusting the chain's bias. Add
the following code to the first element of a chain:
app:layout_constraintVertical_bias="0.2"
In a vertical chain, the first element is a top-most view, and in a horizontal chain it is the left-most
view. The first element defines the whole chain's behavior.
Chains are a new feature and are updated frequently. Here is an official Android Documentation
on Chains.
https://riptutorial.com/ 308
Chapter 58: ConstraintSet
Introduction
This class allows you to define programmatically a set of constraints to be used with
ConstraintLayout. It lets you create and save constraints, and apply them to an existing
ConstraintLayout.
Examples
ConstraintSet with ContraintLayout Programmatically
import android.content.Context;
import android.os.Bundle;
import android.support.constraint.ConstraintLayout;
import android.support.constraint.ConstraintSet;
import android.support.transition.TransitionManager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = this;
mConstraintSet2.clone(context, R.layout.state2); // get constraints from layout
setContentView(R.layout.state1);
mConstraintLayout = (ConstraintLayout) findViewById(R.id.activity_main);
mConstraintSet1.clone(mConstraintLayout); // get constraints from ConstraintSet
}
https://riptutorial.com/ 309
Chapter 59: ContentProvider
Remarks
Content providers manage access to a structured set of data. They encapsulate the data, and
provide mechanisms for defining data security. Content providers are the standard interface that
connects data in one process with code running in another process.
When you want to access data in a content provider, you use the ContentResolver object in your
application's Context to communicate with the provider as a client. The ContentResolver object
communicates with the provider object, an instance of a class that implements ContentProvider.
The provider object receives data requests from clients, performs the requested action, and
returns the results.
You don't need to develop your own provider if you don't intend to share your data with other
applications. However, you do need your own provider to provide custom search suggestions in
your own application. You also need your own provider if you want to copy and paste complex
data or files from your application to other applications.
Android itself includes content providers that manage data such as audio, video, images, and
personal contact information. You can see some of them listed in the reference documentation for
the android.provider package. With some restrictions, these providers are accessible to any
Android application.
Examples
Implementing a basic content provider class
A contract class defines constants that help applications work with the content URIs, column
names, intent actions, and other features of a content provider. Contract classes are not included
automatically with a provider; the provider's developer has to define them and then make them
available to other developers.
A provider usually has a single authority, which serves as its Android-internal name. To avoid
conflicts with other providers, use a unique content authority. Because this recommendation is
also true for Android package names, you can define your provider authority as an extension of
the name of the package containing the provider. For example, if your Android package name is
com.example.appname, you should give your provider the authority com.example.appname.provider.
https://riptutorial.com/ 310
A content URI is a URI that identifies data in a provider. Content URIs include the symbolic name
of the entire provider (its authority) and a name that points to a table or file (a path). The optional
id part points to an individual row in a table. Every data access method of ContentProvider has a
content URI as an argument; this allows you to determine the table, row, or file to access. Define
these in the contract class.
@Override
public void onCreate(SQLiteDatabase db) {
// Called when the database is created for the first time. This is where the
// creation of tables and the initial population of the tables should happen.
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Called when the database needs to be upgraded. The implementation
// should use this method to drop tables, add tables, or do anything else it
// needs to upgrade to the new schema version.
}
}
A UriMatcher maps an authority and path to an integer value. The method match() returns a unique
integer value for a URI (it can be any arbitrary number, as long as it's unique). A switch statement
chooses between querying the entire table, and querying for a single record. Our UriMatcher
returns 100 if the URI is the Content URI of Table and 101 if the URI points to a specific row within
https://riptutorial.com/ 311
that table. You can use the # wildcard to match with any number and * to match with any string.
IMPORTANT: the ordering of addURI() calls matters! The UriMatcher will look in sequential order
from first added to last. Since wildcards like # and * are greedy, you will need to make sure that
you have ordered your URIs correctly. For example:
is the proper ordering, since the matcher will look for /example first before resorting to the /* match.
If these method calls were reversed and you called uriMatcher.match("/example"), then the
UriMatcher will stop looking for matches once it encounters the /* path and return the wrong
result!
onCreate(): Initialize your provider. The Android system calls this method immediately after it
creates your provider. Notice that your provider is not created until a ContentResolver object tries
to access it.
@Override
public boolean onCreate() {
dbhelper = new DatabaseHelper(getContext());
return true;
}
@Override
public String getType(Uri uri) {
final int match = matcher.match(uri);
switch (match) {
case DATA_TABLE:
return ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + MyContract.CONTENT_AUTHORITY +
"/" + MyContract.PATH_DATATABLE;
case DATA_TABLE_DATE:
return ContentResolver.ANY_CURSOR_ITEM_TYPE + "/" + MyContract.CONTENT_AUTHORITY +
"/" + MyContract.PATH_DATATABLE;
default:
throw new UnsupportedOperationException("Unknown Uri: " + uri);
}
}
query(): Retrieve data from your provider. Use the arguments to select the table to query, the
rows and columns to return, and the sort order of the result. Return the data as a Cursor object.
https://riptutorial.com/ 312
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Cursor retCursor = dbHelper.getReadableDatabase().query(
MyContract.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
retCursor.setNotificationUri(getContext().getContentResolver(), uri);
return retCursor;
}
Insert a new row into your provider. Use the arguments to select the destination table and to get
the column values to use. Return a content URI for the newly-inserted row.
@Override
public Uri insert(Uri uri, ContentValues values)
{
final SQLiteDatabase db = dbHelper.getWritableDatabase();
long id = db.insert(MyContract.TABLE_NAME, null, values);
return ContentUris.withAppendedId(MyContract.CONTENT_URI, ID);
}
delete(): Delete rows from your provider. Use the arguments to select the table and the rows to
delete. Return the number of rows deleted.
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int rowsDeleted = db.delete(MyContract.TABLE_NAME, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
return rowsDeleted;
}
update(): Update existing rows in your provider. Use the arguments to select the table and rows to
update and to get the new column values. Return the number of rows updated.
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int rowsUpdated = db.update(MyContract.TABLE_NAME, values, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
<provider
android:authorities="com.example.myApp"
android:name=".DatabaseProvider"/>
https://riptutorial.com/ 313
Chapter 60: Context
Introduction
Per Google documentation: "Interface to global information about an application environment. It
allows access to application-specific resources and classes, as well as up-calls for application-
level operations such as launching activities, broadcasting and receiving intents, etc."
More simply put, Context is the current state of your application. It allows you to provide
information to objects so that they can be aware of what is going on in other parts of your
application.
Syntax
• getApplicationContext()
• getBaseContext()
• getContext()
• this
Remarks
This StackOverflow page has several comprehensive and well written explanations of the concept
of Context:
What is Context?
Examples
Basic Examples
this (when in a class that extends from Context, such as the Application, Activity, Service and
IntentService classes)
https://riptutorial.com/ 314
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
https://riptutorial.com/ 315
Chapter 61: Convert vietnamese string to
english string Android
Examples
example:
converted:
https://riptutorial.com/ 316
Chapter 62: CoordinatorLayout and
Behaviors
Introduction
The CoordinatorLayout is a super-powered FrameLayout and goal of this ViewGroup is to
coordinate the views that are inside it.
The main appeal of the CoordinatorLayout is its ability to coordinate the animations and transitions
of the views within the XML file itself.
:As a container for a specific interaction with one or more child views
Remarks
The CoordinatorLayout is a container that extends the FrameLayout.
By attaching a CoordinatorLayout.Behavior to a direct child of CoordinatorLayout, you’ll be able to
intercept touch events, window insets, measurement, layout, and nested scrolling.
By specifying Behaviors for child views of a CoordinatorLayout you can provide many different
interactions within a single parent and those views can also interact with one another. View
classes can specify a default behavior when used as a child of a CoordinatorLayout using the
DefaultBehavior annotation.
Examples
Creating a simple Behavior
/**
* Default constructor.
*/
public MyBehavior() {
https://riptutorial.com/ 317
}
/**
* Default constructor for inflating a MyBehavior from layout.
*
* @param context The {@link Context}.
* @param attrs The {@link AttributeSet}.
*/
public MyBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
<View
android:layout_height="...."
android:layout_width="...."
app:layout_behavior=".MyBehavior" />
@CoordinatorLayout.DefaultBehavior(MyBehavior.class)
public class MyView extends ..... {
The SwipeDismissBehavior works on any View and implements the functionality of swipe to dismiss
in our layouts with a CoordinatorLayout.
Just use:
https://riptutorial.com/ 318
final SwipeDismissBehavior<MyView> swipe = new SwipeDismissBehavior();
@Override
public void onDragStateChanged(int state) {
//......
}
});
You can use the CoordinatorLayout.Behavior to create dependencies between views. You can
anchor a View to another View by:
For example, in order to create a Behavior for moving an ImageView when another one is moved
(example Toolbar), perform the following steps:
• Override the layoutDependsOn method returning true. This method is called every time a
change occurs to the layout:
@Override
public boolean layoutDependsOn(CoordinatorLayout parent,
ImageView child, View dependency) {
// Returns true to add a dependency.
return dependency instanceof Toolbar;
}
@Override
https://riptutorial.com/ 319
public boolean onDependentViewChanged(CoordinatorLayout parent, ImageView child, View
dependency) {
// Implement here animations, translations, or movements; always related to the
provided dependency.
float translationY = Math.min(0, dependency.getTranslationY() -
dependency.getHeight());
child.setTranslationY(translationY);
}
https://riptutorial.com/ 320
Chapter 63: Count Down Timer
Parameters
Parameter Details
The total duration the timer will run for, a.k.a how far in the future you
long millisInFuture
want the timer to end. In milliseconds.
long The interval at which you would like to receive timer updates. In
countDownInterval milliseconds.
Remarks
CountDownTimer is a pretty lean class - it does one thing very well. Since you can only
start/cancel a CountDownTimer, you have to implement pause/resume functionality as shown in
the second example. For more complex functionality, or to specify a timer that should run
indefinitely, use the Timer object.
Examples
Creating a simple countdown timer
CountDownTimer is useful for repeatedly performing an action in a steady interval for a set
duration. In this example, we will update a text view every second for 30 seconds telling how much
time is remaining. Then when the timer finishes, we will set the TextView to say "Done."
In this example, we will pause/resume the CountDownTimer based off of the Activity lifecycle.
https://riptutorial.com/ 321
private static final long TIMER_DURATION = 60000L;
private static final long TIMER_INTERVAL = 1000L;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
@Override
public void onTick(long millisUntilFinished) {
textView.setText(String.format(Locale.getDefault(), "%d sec.", millisUntilFinished
/ 1000L));
mTimeRemaining = millisUntilFinished; // Saving timeRemaining in Activity for
pause/resume of CountDownTimer.
}
@Override
public void onFinish() {
textView.setText("Done.");
}
}.start();
}
@Override
protected void onResume() {
super.onResume();
@Override
public void onFinish() {
textView.setText("Done.");
}
}.start();
}
}
@Override
protected void onPause() {
super.onPause();
mCountDownTimer.cancel();
mCountDownTimer = null;
}
https://riptutorial.com/ 322
Read Count Down Timer online: https://riptutorial.com/android/topic/6063/count-down-timer
https://riptutorial.com/ 323
Chapter 64: Crash Reporting Tools
Remarks
The best complete wiki is available here in github.
Examples
Fabric - Crashlytics
Fabric is a modular mobile platform that provides useful kits you can mix to build your application.
Crashlytics is a crash and issue reporting tool provided by Fabric that allows you to track and
monitor your applications in detail.
buildscript {
repositories {
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
// The Fabric Gradle plugin uses an open ended version to react
// quickly to Android tooling updates
classpath 'io.fabric.tools:gradle:1.+'
}
}
repositories {
maven { url 'https://maven.fabric.io/public' }
}
https://riptutorial.com/ 324
dependencies {
compile('com.crashlytics.sdk.android:crashlytics:2.6.6@aar') {
transitive = true;
}
}
Step 2: Add Your API Key and the INTERNET permission in AndroidManifest.xml
<meta-data
android:name="io.fabric.ApiKey"
android:value="25eeca3bb31cd41577e097cabd1ab9eee9da151d"
/>
</application>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
https://riptutorial.com/ 325
After installing the plugin, restart Android Studio and login with your account using Android
Studio.
https://riptutorial.com/ 326
Then it will show the projects that you have / the project you opened, select the one you need and
click next .. next.
Select the kit you would like to add, for his example it is Crashlytics :
https://riptutorial.com/ 327
Then hit Install. You don't need to add it manually this time like above gradle plugin, instead it
will build for you.
https://riptutorial.com/ 328
Done!
Step 1: Add the dependency of latest ACRA AAR to your application gradle(build.gradle).
Step 2: In your application class(the class which extends Application; if not create it) Add a
@ReportsCrashes annotation and override the attachBaseContext() method.
@ReportsCrashes(
formUri = "Your choice of backend",
reportType = REPORT_TYPES(JSON/FORM),
httpMethod = HTTP_METHOD(POST/PUT),
formUriBasicAuthLogin = "AUTH_USERNAME",
formUriBasicAuthPassword = "AUTH_PASSWORD,
customReportContent = {
ReportField.USER_APP_START_DATE,
https://riptutorial.com/ 329
ReportField.USER_CRASH_DATE,
ReportField.APP_VERSION_CODE,
ReportField.APP_VERSION_NAME,
ReportField.ANDROID_VERSION,
ReportField.DEVICE_ID,
ReportField.BUILD,
ReportField.BRAND,
ReportField.DEVICE_FEATURES,
ReportField.PACKAGE_NAME,
ReportField.REPORT_ID,
ReportField.STACK_TRACE,
},
mode = NOTIFICATION_TYPE(TOAST,DIALOG,NOTIFICATION)
resToastText = R.string.crash_text_toast)
Where AUTH_USERNAME and AUTH_PASSWORD are the credentials of your desired backends
.
<application
android:name=".MyApplication">
<service></service>
<activity></activity>
<receiver></receiver>
</application>
Step 5: Make sure you have internet permission to receive the report from crashed application
<uses-permission android:name="android.permission.INTERNET"/>
In case if you want to send the silent report to the backend then just use the below method to
achieve it.
ACRA.getErrorReporter().handleSilentException(e);
Add a button you can tap to trigger a crash. Paste this code into your layout where you’d like the
button to appear.
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Force Crash!"
https://riptutorial.com/ 330
android:onClick="forceCrash"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
Throw a RuntimeException
Run your app and tap the new button to cause a crash. In a minute or two you should be able to
see the crash on your Crashlytics dashboard as well as you will get a mail.
Sherlock captures all your crashes and reports them as a notification. When you tap on the
notification, it opens up an activity with all the crash details along with Device and Application info
dependencies {
compile('com.github.ajitsing:sherlock:1.0.1@aar') {
transitive = true
}
}
After syncing your android studio, initialize Sherlock in your Application class.
package com.singhajit.login;
import android.app.Application;
import com.singhajit.sherlock.core.Sherlock;
Thats all you need to do. Also Sherlock does much more than just reporting a crash. To checkout
all its features take a look at this article.
Demo
https://riptutorial.com/ 331
Read Crash Reporting Tools online: https://riptutorial.com/android/topic/3871/crash-reporting-tools
https://riptutorial.com/ 332
Chapter 65: Create Android Custom ROMs
Examples
Making Your Machine Ready for Building!
Before you can build anything, you are required to make your machine ready for building. For this
you need to install a lot of libraries and modules. The most recommended Linux distribution is
Ubuntu, so this example will focus on installing everything that is needed on Ubuntu.
Installing Java
First, add the following Personal Package Archive (PPA): sudo apt-add-repository ppa:openjdk-
r/ppa.
sudo apt-get install git-core python gnupg flex bison gperf libsdl1.2-dev libesd0-dev
libwxgtk2.8-dev squashfs-tools build-essential zip curl libncurses5-dev zlib1g-dev openjdk-8-
jre openjdk-8-jdk pngcrush schedtool libxml2 libxml2-utils xsltproc lzop libc6-dev schedtool
g++-multilib lib32z1-dev lib32ncurses5-dev gcc-multilib liblz4-* pngquant ncurses-dev texinfo
gcc gperf patch libtool automake g++ gawk subversion expat libexpat1-dev python-all-dev
binutils-static bc libcloog-isl-dev libcap-dev autoconf libgmp-dev build-essential gcc-
multilib g++-multilib pkg-config libmpc-dev libmpfr-dev lzma* liblzma* w3m android-tools-adb
maven ncftp figlet
Finally, let us set up the cache and the repo by the following commands:
https://riptutorial.com/ 333
sudo install utils/repo /usr/bin/
sudo install utils/ccache /usr/bin/
Please note: We can also achieve this setup by running the automated scripts made by Akhil
Narang (akhilnarang), one of the maintainers of Resurrection Remix OS. These scripts can be
found on GitHub.
https://riptutorial.com/ 334
Chapter 66: Create Singleton Class for Toast
Message
Introduction
Toast messages are the most simple way of providing feedback to the user. By default, Android
provide gray color message toast where we can set the message and the duration of the
message. If we need to create more customizable and reusable toast message, we can implement
it by ourselves with the use of a custom layout. More importantly when we are implementing it, the
use of Singelton design pattern will make it easy for maintaining and development of the custom
toast message class.
Syntax
• Toast Toast(Context contex)
• void setDuration(int duration)
• void setGravity(int gravity, int xOffset, int yOffset)
• void setView(View view)
• void show()
Parameters
Parameter details
Relevant context which needs to display your toast message. If you use this in
context
the activity pass "this" keyword or If you use in fragement pass as "getActivity()".
view Create a custom view and pass that view object to this.
Pass the gravity position of the toaster. All the position has added under the
gravity Gravity class as the static variables . The Most common positions are
Gravity.TOP, Gravity.BOTTOM, Gravity.LEFT, Gravity.RIGHT.
Remarks
Toast message is a simple way of providing feedback to user about something is happening. If
https://riptutorial.com/ 335
you need a more advanced way to give feedback you can use dialogs or snackbar.
To get more details about the toast message please check this documentation.
https://developer.android.com/reference/android/widget/Toast.html
Examples
Create own singleton class for toast massages
Here is how to create your own singleton class for toast messages, If your application need to
show success, warning and the danger messages for different use cases you can use this class
after you have modified it to your own specifications.
switch (type){
case 0:
//if the message type is 0 fail toaster method will call
createFailToast(toastLayout,toastShowMessage,message);
break;
case 1:
//if the message type is 1 success toaster method will call
createSuccessToast(toastLayout,toastShowMessage,message);
break;
case 2:
createWarningToast( toastLayout, toastShowMessage, message);
//if the message type is 2 warning toaster method will call
break;
default:
createFailToast(toastLayout,toastShowMessage,message);
}
}
https://riptutorial.com/ 336
//Failure toast message method
private final void createFailToast(LinearLayout toastLayout,TextView
toastMessage,String message){
toastLayout.setBackgroundColor(context.getResources().getColor(R.color.button_alert_normal));
toastMessage.setText(message);
toastMessage.setTextColor(context.getResources().getColor(R.color.white));
showToast(context,toastLayout);
}
toastLayout.setBackgroundColor(context.getResources().getColor(R.color.warning_toast));
toastMessage.setText(message);
toastMessage.setTextColor(context.getResources().getColor(R.color.white));
showToast(context, toastLayout);
}
//success toast message method
private final void createSuccessToast(LinearLayout toastLayout,TextView
toastMessage,String message){
toastLayout.setBackgroundColor(context.getResources().getColor(R.color.success_toast));
toastMessage.setText(message);
toastMessage.setTextColor(context.getResources().getColor(R.color.white));
showToast(context,toastLayout);
}
https://riptutorial.com/ 337
Chapter 67: Creating Custom Views
Examples
Creating Custom Views
If you need a completely customized view, you'll need to subclass View (the superclass of all
Android views) and provide your custom sizing (onMeasure(...)) and drawing (onDraw(...))
methods:
1. Create your custom view skeleton: this is basically the same for every custom view. Here
we create the skeleton for a custom view that can draw a smiley, called SmileyView:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {/* ... */}
@Override
protected void onDraw(Canvas canvas) {/* ... */}
}
2. Initialize your paints: the Paint objects are the brushes of your virtual canvas defining how
your geometric objects are rendered (e.g. color, fill and stroke style, etc.). Here we create
two Paints, one yellow filled paint for the circle and one black stroke paint for the eyes and
the mouth:
https://riptutorial.com/ 338
mCirclePaint.setColor(Color.YELLOW);
mEyeAndMouthPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mEyeAndMouthPaint.setStyle(Paint.Style.STROKE);
mEyeAndMouthPaint.setStrokeWidth(16 * getResources().getDisplayMetrics().density);
mEyeAndMouthPaint.setStrokeCap(Paint.Cap.ROUND);
mEyeAndMouthPaint.setColor(Color.BLACK);
}
3. Implement your own onMeasure(...) method: this is required so that the parent layouts
(e.g. FrameLayout) can properly align your custom view. It provides a set of measureSpecs that
you can use to determine your view's height and width. Here we create a square by making
sure that the height and width are the same:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int w = MeasureSpec.getSize(widthMeasureSpec);
int h = MeasureSpec.getSize(heightMeasureSpec);
Note that onMeasure(...) must contain at least one call to setMeasuredDimension(..) or else
your custom view will crash with an IllegalStateException.
4. Implement your own onSizeChanged(...) method: this allows you to catch the current height
and width of your custom view to properly adjust your rendering code. Here we just calculate
our center and our radius:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCenterX = w / 2f;
mCenterY = h / 2f;
mRadius = Math.min(w, h) / 2f;
}
5. Implement your own onDraw(...) method: this is where you implement the actual rendering
of your view. It provides a Canvas object that you can draw on (see the official Canvas
documentation for all drawing methods available).
@Override
protected void onDraw(Canvas canvas) {
// draw face
canvas.drawCircle(mCenterX, mCenterY, mRadius, mCirclePaint);
// draw eyes
float eyeRadius = mRadius / 5f;
float eyeOffsetX = mRadius / 3f;
float eyeOffsetY = mRadius / 3f;
canvas.drawCircle(mCenterX - eyeOffsetX, mCenterY - eyeOffsetY, eyeRadius,
mEyeAndMouthPaint);
canvas.drawCircle(mCenterX + eyeOffsetX, mCenterY - eyeOffsetY, eyeRadius,
mEyeAndMouthPaint);
// draw mouth
float mouthInset = mRadius /3f;
https://riptutorial.com/ 339
mArcBounds.set(mouthInset, mouthInset, mRadius * 2 - mouthInset, mRadius * 2 -
mouthInset);
canvas.drawArc(mArcBounds, 45f, 90f, false, mEyeAndMouthPaint);
}
6. Add your custom view to a layout: the custom view can now be included in any layout files
that you have. Here we just wrap it inside a FrameLayout:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.app.SmileyView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Note that it is recommended to build your project after the view code is finished. Without building it
you won't be able to see the view on a preview screen in Android Studio.
After putting everything together, you should be greeted with the following screen after launching
the activity containing the above layout:
Custom views can also take custom attributes which can be used in Android layout resource files.
To add attributes to your custom view you need to do the following:
1. Define the name and type of your attributes: this is done inside res/values/attrs.xml
(create it if necessary). The following file defines a color attribute for our smiley's face color
https://riptutorial.com/ 340
and an enum attribute for the smiley's expression:
<resources>
<declare-styleable name="SmileyView">
<attr name="smileyColor" format="color" />
<attr name="smileyExpression" format="enum">
<enum name="happy" value="0"/>
<enum name="sad" value="1"/>
</attr>
</declare-styleable>
<!-- attributes for other views -->
</resources>
2. Use your attributes inside your layout: this can be done inside any layout files that use
your custom view. The following layout file creates a screen with a happy yellow smiley:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.example.app.SmileyView
android:layout_height="56dp"
android:layout_width="56dp"
app:smileyColor="#ffff00"
app:smileyExpression="happy" />
</FrameLayout>
Tip: Custom attributes do not work with the tools: prefix in Android Studio 2.1 and older (and
possibly in future versions). In this example, replacing app:smileyColor with tools:smileyColor
would result in smileyColor neither being set during runtime nor at design time.
3. Read your attributes: this is done inside your custom view source code. The following
snippet of SmileyView demonstrates how the attributes can be extracted:
https://riptutorial.com/ 341
// initPaints(); ...
}
}
4. (Optional) Add default style: this is done by adding a style with the default values and
loading it inside your custom view. The following default smiley style represents a happy
yellow one:
Which gets applied in our SmileyView by adding it as the last parameter of the call to
obtainStyledAttributes (see code in step 3):
Note that any attribute values set in the inflated layout file (see code in step 2) will override
the corresponding values of the default style.
5. (Optional) Provide styles inside themes: this is done by adding a new style reference
attribute which can be used inside your themes and providing a style for that attribute. Here
we simply name our reference attribute smileyStyle:
Which we then provide a style for in our app theme (here we just reuse the default style from
step 4):
A compound view is a custom ViewGroup that's treated as a single view by the surrounding
program code. Such a ViewGroup can be really useful in DDD-like design, because it can
correspond to an aggregate, in this example, a Contact. It can be reused everywhere that contact
is displayed.
This means that the surrounding controller code, an Activity, Fragment or Adapter, can simply
pass the data object to the view without picking it apart into a number of different UI widgets.
This facilitates code reuse and makes for a better design according to SOLID priciples.
https://riptutorial.com/ 342
The layout XML
This is usually where you start. You have an existing bit of XML that you find yourself reusing,
perhaps as an <include/>. Extract it into a separate XML file and wrap the root tag in a <merge>
element:
<ImageView
android:id="@+id/photo"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentRight="true" />
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/photo" />
<TextView
android:id="@+id/phone_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/name"
android:layout_toLeftOf="@id/photo" />
</merge>
This XML file keeps working in the Layout Editor in Android Studio perfectly fine. You can treat it
like any other layout.
Once you have the XML file, create the custom view group.
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.ImageView;
import android.widget.TextView;
import myapp.R;
/**
* A compound view to show contacts.
*
* This class can be put into an XML layout or instantiated programmatically, it
* will work correctly either way.
*/
public class ContactView extends RelativeLayout {
https://riptutorial.com/ 343
// This class extends RelativeLayout because that comes with an automatic
// (MATCH_PARENT, MATCH_PARENT) layout for its child item. You can extend
// the raw android.view.ViewGroup class if you want more control. See the
// note in the layout XML why you wouldn't want to extend a complex view
// such as RelativeLayout.
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ContactView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
// 3. Define a setter that's expressed in your domain model. This is what the example is
// all about. All controller code can just invoke this setter instead of fiddling with
// lots of strings, visibility options, colors, animations, etc. If you don't use a
// custom view, this code will usually end up in a static helper method (bad) or copies
The init(Context, AttributeSet) method is where you would read any custom XML attributes as
explained in Adding Attributes to Views.
Usage in XML
https://riptutorial.com/ 344
Here's an example fragment_contact_info.xml that illustrates how you'd put a single ContactView
on top of a list of messages:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- The compound view becomes like any other view XML element -->
<myapp.ContactView
android:id="@+id/contact"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/message_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
Usage in Code
Here's an example RecyclerView.Adapter that shows a list of contacts. This example illustrates just
how much cleaner the controller code gets when it's completely free of View manipulation.
package myapp;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
@Override
public ContactsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ContactView v = new ContactView(context); // <--- this
return new ContactsViewHolder(v);
}
@Override
public void onBindViewHolder(ContactsViewHolder holder, int position) {
Contact contact = this.getItem(position);
holder.setContact(contact); // <--- this
}
https://riptutorial.com/ 345
public void setContact(Contact contact) {
((ContactView) itemView).setContact(contact); // <--- this
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint(); //Do not allocate here
}
drawable.setBounds(boundsRect);
drawable.draw(canvas);
Do not redraw the entire view to update just a small part of it. Instead redraw the specific part of
view.
invalidate(boundToBeRefreshed);
If your view is doing some continuous animation, for instance a watch-face showing each and
every second, at least stop the animation at onStop() of the activity and start it back on onStart() of
the activity.
Do not do any calculations inside the onDraw method of a view, you should instead finish drawing
before calling invalidate(). By using this technique you can avoid frame dropping in your view.
Rotations
The basic operations of a view are translate, rotate, etc... Almost every developer has faced this
problem when they use bitmap or gradients in their custom view. If the view is going to show a
rotated view and the bitmap has to be rotated in that custom view, many of us will think that it will
be expensive. Many think that rotating a bitmap is very expensive because in order to do that, you
need to translate the bitmap's pixel matrix. But the truth is that it is not that tough! Instead of
rotating the bitmap, just rotate the canvas itself!
https://riptutorial.com/ 346
// Rotate the canvas by providing the center point as pivot and angle
canvas.rotate(pivotX, pivotY, angle);
// Draw whatever you want
// Basically whatever you draw here will be drawn as per the angle you rotated the canvas
canvas.drawBitmap(...);
// Now restore your your canvas to its original state
canvas.restore(save);
// Unless canvas is restored to its original state, further draw will also be rotated.
Main motive to develop this compound view is, below 5.0 devices does not support svg in
drawable inside TextView/EditText. One more pros is, we can set height and width of drawableRight
inside EditText. I have separated it from my project and created in separate module.
build.gradle
dependencies {
compile 'com.android.support:appcompat-v7:25.3.1'
}
<EditText
android:id="@+id/edt_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:maxLines="1"
android:paddingEnd="40dp"
android:paddingLeft="5dp"
android:paddingRight="40dp"
android:paddingStart="5dp" />
https://riptutorial.com/ 347
android:layout_gravity="right|center_vertical"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp" />
</FrameLayout>
Code : EditTextWithDrawable.java
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public EditTextWithDrawable(Context context, AttributeSet attrs, int defStyleAttr, int
defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
https://riptutorial.com/ 348
int drawableRes =
attributeArray.getResourceId(
R.styleable.EditTextWithDrawable_c_e_d_drawableRightSVG, -1);
if (drawableRes != -1) {
mDrawableRight.setImageResource(drawableRes);
}
mEditText.setHint(attributeArray.getString(
R.styleable.EditTextWithDrawable_c_e_d_hint));
mEditText.setTextColor(attributeArray.getColor(
R.styleable.EditTextWithDrawable_c_e_d_textColor, Color.BLACK));
int textSize =
attributeArray.getDimensionPixelSize(R.styleable.EditTextWithDrawable_c_e_d_textSize, 15);
mEditText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
android.view.ViewGroup.LayoutParams layoutParams =
mDrawableRight.getLayoutParams();
layoutParams.width = (textSize * 3) / 2;
layoutParams.height = (textSize * 3) / 2;
mDrawableRight.setLayoutParams(layoutParams);
attributeArray.recycle();
}
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.customeditdrawable.AppEditTextWithDrawable
android:id="@+id/edt_search_emp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:c_e_d_drawableRightSVG="@drawable/ic_svg_search"
app:c_e_d_hint="@string/hint_search_here"
app:c_e_d_textColor="@color/text_color_dark_on_light_bg"
app:c_e_d_textSize="@dimen/text_size_small" />
</LinearLayout>
Activity : MainActivity.java
https://riptutorial.com/ 349
Responding to Touch Events
Many custom views need to accept user interaction in the form of touch events. You can get
access to touch events by overriding onTouchEvent. There are a number of actions you can filter
out. The main ones are
• ACTION_DOWN:
This is triggered once when your finger first touches the view.
• ACTION_MOVE: This is called every time your finger moves a little across the view. It gets called
many times.
• ACTION_UP: This is the last action to be called as you lift your finger off the screen.
You can add the following method to your view and then observe the log output when you touch
and move your finger around your view.
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.i("CustomView", "onTouchEvent: ACTION_DOWN: x = " + x + ", y = " + y);
break;
case MotionEvent.ACTION_MOVE:
Log.i("CustomView", "onTouchEvent: ACTION_MOVE: x = " + x + ", y = " + y);
break;
case MotionEvent.ACTION_UP:
Log.i("CustomView", "onTouchEvent: ACTION_UP: x = " + x + ", y = " + y);
break;
}
return true;
}
Further reading:
https://riptutorial.com/ 350
Chapter 68: Creating Overlay (always-on-top)
Windows
Examples
Popup overlay
In order to put your view on top of every application, you have to assign your view to the
corresponding window manager. For that you need the system alert permission, which can be
requested by adding the following line to your manifest file:
Note: If your application gets destroyed, your view will be removed from the window manager.
Therefore, it is better to create the view and assign it to the window manager by a foreground
service.
In order to define the position of your view, you have to create some layout parameters as follows:
Now, you can assign your view together with the created layout parameters to the window
manager instance as follows:
mWindowManager.addView(yourView, mLayoutParams);
Voila! Your view has been successfully placed on top of all other applications.
https://riptutorial.com/ 351
From android 6.0 this permission needs to grant dynamically,
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Solution :-
if(!Settings.canDrawOverlays(this)){
// ask for setting
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_OVERLAY_PERMISSION);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_OVERLAY_PERMISSION) {
if (Settings.canDrawOverlays(this)) {
// permission granted...
}else{
// permission not granted...
}
}
}
https://riptutorial.com/ 352
Chapter 69: Creating Splash screen
Remarks
The first example(a basic splash screen) is not the most efficient way to handle it. As such, it is
basic splash screen.
Examples
A basic splash screen
A splash screen is just like any other activity, but it can handle all of your startup-needs in the
background. Example:
Manifest:
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".Splash"
android:label="@string/app_name"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</application>
</manifest>
Here is an example splashscreen that also handles some critical app elements:
https://riptutorial.com/ 353
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WAKE_LOCK) !=
PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,Manifest.permission.INTERNET) !=
PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_NETWORK_STATE) != PackageManager.PERMISSION_GRANTED) {//Can add
more as per requirement
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WAKE_LOCK,
Manifest.permission.INTERNET,
Manifest.permission.ACCESS_NETWORK_STATE},
123);
}
}
@Override
protected void onCreate(Bundle sis){
super.onCreate(sis);
//set the content view. The XML file can contain nothing but an image, such as a logo
or the app icon
setContentView(R.layout.splash);
//we want to display the splash screen for a few seconds before it automatically
//disappears and loads the game. So we create a thread:
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//request permissions. NOTE: Copying this and the manifest will cause the app
to crash as the permissions requested aren't defined in the manifest.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {
checkPermission();
}
String lang = [load or determine the system language and set to default if
it isn't available.]
Locale locale = new Locale(lang);
Locale.setDefault(locale);
Configuration config = new Configuration ();
config.locale = locale;
Splash.this.getResources().updateConfiguration(config,
Splash.this.getResources().getDisplayMetrics()) ;
https://riptutorial.com/ 354
}
This example shows a simple but effective splash screen with animation that can be created by
using Android Studio.
https://riptutorial.com/ 355
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
startActivity(new Intent(this,HomeActivity.class));
// HomeActivity.class is the activity to go after showing the splash screen.
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
imageView.startAnimation(anim);
}
}
<activity
android:name=".Splash"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Then, remove the default launcher activity by removing the following code from the
https://riptutorial.com/ 356
AndroidManifest file:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
https://riptutorial.com/ 357
Chapter 70: Creating your own libraries for
Android applications
Examples
Creating library project
To create a libary , you should use File -> New -> New Module -> Android Library. This will create a
basic library project.
When that's done, you must have a project that is set up the following manner:
[libs]
[src]
[main]
[java]
[library package]
[test]
[java]
[library package]
build.gradle //"app"-level
proguard-rules.pro
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
minSdkVersion 14
targetSdkVersion 23
https://riptutorial.com/ 358
}
}
To use the library, you must include it as a dependency with the following line:
3. Modify your library project's build.gradle file by adding the following code:
...
artifacts {
archives sourcesJar
archives javadocJar
}
https://riptutorial.com/ 359
6. Your library is now available by the following dependency:
https://riptutorial.com/ 360
Chapter 71: Custom Fonts
Examples
Putting a custom font in your app
Initializing a font
TextViewPlus.java:
https://riptutorial.com/ 361
TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.TextViewPlus);
String customFont = a.getString(R.styleable.TextViewPlus_customFont);
setCustomFont(ctx, customFont);
a.recycle();
}
setTypeface(typeface);
return true;
}
}
How to use:
<com.mypackage.TextViewPlus
android:id="@+id/textViewPlus1"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:text="@string/showingOffTheNewTypeface"
foo:customFont="my_font_name_regular.otf">
</com.mypackage.TextViewPlus>
</LinearLayout>
https://riptutorial.com/ 362
Efficient Typeface loading
Loading custom fonts can be lead to a bad performance. I highly recommend to use this little
helper which saves/loads your already used fonts into a Hashtable.
/**
* Get typeface by filename from assets main directory
*
* @param context
* @param fileName the name of the font file in the asset main directory
* @return
*/
public static Typeface getTypeFace(final Context context, final String fileName) {
Typeface tempTypeface = sTypeFaces.get(fileName);
if (tempTypeface == null) {
tempTypeface = Typeface.createFromAsset(context.getAssets(), fileName);
sTypeFaces.put(fileName, tempTypeface);
}
return tempTypeface;
}
Usage:
https://riptutorial.com/ 363
Then in your activity, in onCreate() method:
Android O introduces a new feature, called Fonts in XML, which allows you to use fonts as
resources. This means, that there is no need to bundle fonts as assets. Fonts are now compiled in
an R file and are automatically available in the system as a resource.
You can also create your own font family by adding the following XML file into the res/font
directory:
You can use both the font file and the font family file in the same way:
• In an XML file, by using the android:fontFamily attribute, for example like this:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/myfont"/>
Or like this:
https://riptutorial.com/ 364
Typeface typeface = getResources().getFont(R.font.myfont);
textView.setTypeface(typeface);
https://riptutorial.com/ 365
Chapter 72: Dagger 2
Syntax
• @Module
• @Component(dependencies={OtherComponent.class}, modules={ModuleA.class,
ModuleB.class})
• DaggerMyComponent.create()
• DaggerMyComponent.builder().myModule(newMyModule()).create()
Remarks
Not to confuse with dagger by square, the predecessor to dagger 2.
Examples
Component setup for Application and Activity injection
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
Context provideContext();
Gson provideGson();
}
A module to use together with the AppComponent which will provide its singleton objects, e.g. an
instance of Gson to reuse throughout the whole application.
@Module
public class AppModule {
@Singleton
@Provides
Gson provideGson() {
return new Gson();
}
https://riptutorial.com/ 366
@Singleton
@Provides
Context provideContext() {
return mApplication;
}
}
@Inject
AppComponent mAppComponent;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder().appModule(new AppModule(this)).build().inject(this);
}
Now an activity scoped component that depends on the AppComponent to gain access to the
singleton objects.
@ActivityScope
@Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface MainActivityComponent {
And a reusable ActivityModule that will provide basic dependencies, like a FragmentManager
@Module
public class ActivityModule {
@ActivityScope
public AppCompatActivity provideActivity() {
return mActivity;
}
@ActivityScope
public FragmentManager provideFragmentManager(AppCompatActivity activity) {
return activity.getSupportFragmentManager();
https://riptutorial.com/ 367
}
}
Putting everything together we're set up and can inject our activity and be sure to use the same
Gson throughout out app!
@Inject
Gson mGson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainActivityComponent.builder()
.appComponent(((App)getApplication()).getAppComponent())
.activityModule(new ActivityModule(this))
.build().inject(this);
}
}
Custom Scopes
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScope {
}
Scopes are just annotations and you can create your own ones where needed.
Constructor Injection
This class can be provided by any component. It has no dependencies itself and is not scoped.
There is no further code necessary.
Dependencies are declared as parameters in the constructor. Dagger will call the constructor and
supply the dependencies, as long as those dependencies can be provided.
https://riptutorial.com/ 368
private Engine engine;
@Inject
public Car(Engine engine) {
this.engine = engine;
}
}
This class can be provided by every component iff this component can also provide all of its
dependencies—Engine in this case. Since Engine can also be constructor injected, any component
can provide a Car.
You can use constructor injection whenever all of the dependencies can be provided by the
component. A component can provide a dependency, if
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
void inject(App app);
Context provideContext();
Gson provideGson();
@ActivityScope
@Subcomponent(modules = ActivityModule.class)
public interface MainActivityComponent {
void inject(MainActivity activity);
}
@Inject
Gson mGson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((App)getApplication()).getAppComponent()
.mainActivityComponent(new ActivityModule(this)).inject(this);
}
}
https://riptutorial.com/ 369
Since the release of Gradle 2.2, the use of the android-apt plugin is no longer used. The following
method of setting up Dagger 2 should be used. For older version of Gradle, use the previous
method shown below.
dependencies {
// apt command comes from the android-apt plugin
annotationProcessor 'com.google.dagger:dagger-compiler:2.8'
compile 'com.google.dagger:dagger:2.8'
provided 'javax.annotation:jsr250-api:1.0'
}
To use Dagger 2 it's necessary to add android-apt plugin, add this to the root build.gradle:
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
android {
…
}
compile "com.google.dagger:dagger:${DAGGER_VERSION}"
apt "com.google.dagger:dagger-compiler:${DAGGER_VERSION}"
}
Reference: https://github.com/codepath/android_guides/wiki/Dependency-Injection-with-Dagger-2
Dagger 2 supports creating a component from multiple modules. You can create your component
this way:
@Singleton
@Component(modules = {GeneralPurposeModule.class, SpecificModule.class})
public interface MyMultipleModuleComponent {
void inject(MyFragment myFragment);
void inject(MyService myService);
https://riptutorial.com/ 370
void inject(MyController myController);
void inject(MyActivity myActivity);
}
The two references modules GeneralPurposeModule and SpecificModule can then be implemented as
follows:
GeneralPurposeModule.java
@Module
public class GeneralPurposeModule {
@Provides
@Singleton
public Retrofit getRetrofit(PropertiesReader propertiesReader, RetrofitHeaderInterceptor
headerInterceptor){
// Logic here...
return retrofit;
}
@Provides
@Singleton
public PropertiesReader getPropertiesReader(){
return new PropertiesReader();
}
@Provides
@Singleton
public RetrofitHeaderInterceptor getRetrofitHeaderInterceptor(){
return new RetrofitHeaderInterceptor();
}
}
SpecificModule.java
@Singleton
@Module
public class SpecificModule {
@Provides @Singleton
public RetrofitController getRetrofitController(Retrofit retrofit){
RetrofitController retrofitController = new RetrofitController();
retrofitController.setRetrofit(retrofit);
return retrofitController;
}
@Provides @Singleton
public MyService getMyService(RetrofitController retrofitController){
MyService myService = new MyService();
myService.setRetrofitController(retrofitController);
return myService;
}
}
During the dependency injection phase, the component will take objects from both modules
according to the needs.
This approach is very useful in terms of modularity. In the example, there is a general purpose
https://riptutorial.com/ 371
module used to instantiate components such as the Retrofit object (used to handle the network
communication) and a PropertiesReader (in charge of handling configuration files). There is also a
specific module that handles the instantiation of specific controllers and service classes in relation
to that specific application component.
https://riptutorial.com/ 372
Chapter 73: Data Binding Library
Remarks
Setup
Before using data binding, you must enable the plugin in your build.gradle.
android {
....
dataBinding {
enabled = true
}
}
Note: Data binding was added to the Android Gradle plugin in version 1.5.0
The data binding plugin generates a binding class name by converting your layout's file name to
Pascal case and adding "Binding" to the end. Thus item_detail_activity.xml will generate a class
named ItemDetailActivityBinding.
Resources
• Official documentation
Examples
Basic text field binding
android {
....
dataBinding {
enabled = true
}
}
Data model
https://riptutorial.com/ 373
}
}
Layout XML
The first step is wrapping your layout in a <layout> tag, adding a <data> element, and adding a
<variable> element for your data model.
Then you can bind XML attributes to fields in the data model using @{model.fieldname}, where model
is the variable's name and fieldname is the field you want to access.
item_detail_activity.xml:
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.name}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.description}"/>
</LinearLayout>
</layout>
For each XML layout file properly configured with bindings, the Android Gradle plugin generates a
corresponding class : bindings. Because we have a layout named item_detail_activity, the
corresponding generated binding class is called ItemDetailActivityBinding.
https://riptutorial.com/ 374
If your model has private methods, the databinding library still allows you to access them in your
view without using the full name of the method.
Data model
Layout XML
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
</layout>
Referencing classes
Data model
Layout XML
https://riptutorial.com/ 375
<import type="android.view.View"/>
<variable name="item" type="com.example.Item"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- We reference the View class to set the visibility of this TextView -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.name}"
android:visibility="@{item.name == null ? View.VISIBLE : View.GONE"/>
</LinearLayout>
</layout>
Note: The package java.lang.* is imported automatically by the system. (The same is made by
JVM for Java)
Databinding in Fragment
Data Model
Layout XML
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.name}"/>
https://riptutorial.com/ 376
</LinearLayout>
</layout>
Fragment
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable
Bundle savedInstanceState) {
FragmentTest binding = DataBindingUtil.inflate(inflater, R.layout.fragment_test,
container, false);
Item item = new Item();
item.setName("Thomas");
binding.setItem(item);
return binding.getRoot();
}
Element Properties
AbsListView android:selectedItemPosition
CalendarView android:date
CompoundButton android:checked
• android:year
DatePicker • android:month
• android:day
EditText android:text
NumberPicker android:value
RadioGroup android:checkedButton
RatingBar android:rating
SeekBar android:progress
TabHost android:currentTab
TextView android:text
• android:hour
TimePicker
• android:minute
ToggleButton android:checked
Switch android:checked
https://riptutorial.com/ 377
Usage
<layout ...>
<data>
<variable type="com.example.myapp.User" name="user"/>
</data>
<RelativeLayout ...>
<EditText android:text="@={user.firstName}" .../>
</RelativeLayout>
</layout>
Notice that the Binding expression @={} has an additional =, which is necessary for the two-way
Binding. It is not possible to use methods in two-way Binding expressions.
It's also possible to use data binding within your RecyclerView Adapter.
Data model
XML Layout
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.name}"/>
Adapter class
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// inflate layout and retrieve binding
ListItemBinding binding = DataBindingUtil.inflate(host.getLayoutInflater(),
https://riptutorial.com/ 378
R.layout.list_item, parent, false);
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Item item = items.get(position);
@Override
public int getItemCount() {
return items.size();
}
ItemViewHolder(ListItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
Layout XML
<data>
<variable
name="handler"
type="com.example.ClickHandler"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
https://riptutorial.com/ 379
android:layout_height="wrap_content"
android:text="click me"
android:onClick="@{handler.onButtonClick}"/>
</RelativeLayout>
</layout>
@Override
public void onButtonClick(View v) {
Toast.makeText(context,"Button clicked",Toast.LENGTH_LONG).show();
}
}
Define Interface
Layout XML
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
https://riptutorial.com/ 380
<variable
name="handler"
type="com.example.ClickHandler"/>
<variable
name="user"
type="com.example.User"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
android:onClick="@{() -> handler.onButtonClick(user)}"/>
</RelativeLayout>
</layout>
Activity code :
@Override
public void onButtonClick(User user) {
Toast.makeText(MainActivity.this,"Welcome " +
user.getName(),Toast.LENGTH_LONG).show();
}
}
For some view listener which is not available in xml code but can be set in java code, it can be
bind with custom binding.
Custom class
https://riptutorial.com/ 381
}
Handler class
XML
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/tools" >
<data>
<variable
name="handler"
type="com.example.Handler" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
bind:autoAdapter="@{handler.roleAdapter}" />
</LinearLayout>
</layout>
The Preview pane displays default values for data binding expressions if provided.
For example :
android:layout_height="@{@dimen/main_layout_height, default=wrap_content}"
It will take wrap_content while designing and will act as a wrap_content in preview pane.
Another example is
https://riptutorial.com/ 382
It will display Preview Text in preview pane but when you run it in device/emulator actual text
binded to it will be displayed
Sometimes we need to perform basic operations like hide/show view based on single value, for
that single variable we cannot create model or it is not good practice to create model for that.
DataBinding supports basic datatypes to perform those oprations.
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="selected"
type="Boolean" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World"
android:visibility="@{selected ? View.VISIBLE : View.GONE}" />
</RelativeLayout>
</layout>
binding.setSelected(true);
Databinding in Dialog
Layout XML
https://riptutorial.com/ 383
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/img"
android:layout_width="match_parent"
android:layout_height="100dp"
app:imageUrl="@{url}"
app:progressbar="@{progressBar}"/>
</LinearLayout>
</layout>
BindingAdapter method
@BindingAdapter({"imageUrl","progressbar"})
public static void loadImage(ImageView view, String imageUrl, ProgressBar progressBar){
Glide.with(view.getContext()).load(imageUrl)
.listener(new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(Exception e, String model,
Target<GlideDrawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model,
Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
progressBar.setVisibility(View.GONE);
return false;
}
}).into(view);
}
https://riptutorial.com/ 384
Chapter 74: Data Encryption/Decryption
Introduction
This topic discusses how encryption and decryption works in Android.
Examples
AES encryption of data using password in a secure way
The following example encrypts a given data block using AES. The encryption key is derived in a
secure way (random salt, 1000 rounds of SHA-256). The encryption uses AES in CBC mode with
random IV.
Note that the data stored in the class EncryptedData (salt, iv, and encryptedData) can be
concatenated to a single byte array. You can then save the data or transmit it to the recipient.
private byte[] decrypt(String password, byte[] salt, byte[] iv, byte[] encryptedData) throws
NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException,
InvalidKeyException, BadPaddingException, IllegalBlockSizeException,
InvalidAlgorithmParameterException {
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, PBK_ITERATIONS);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(PBE_ALGORITHM);
Key key = secretKeyFactory.generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
return cipher.doFinal(encryptedData);
https://riptutorial.com/ 385
}
The following example code shows how to test encryption and decryption:
try {
String password = "test12345";
byte[] data = "plaintext11223344556677889900".getBytes("UTF-8");
EncryptedData encData = encrypt(password, data);
byte[] decryptedData = decrypt(password, encData.salt, encData.iv, encData.encryptedData);
String decDataAsString = new String(decryptedData, "UTF-8");
Toast.makeText(this, decDataAsString, Toast.LENGTH_LONG).show();
} catch (Exception e) {
e.printStackTrace();
}
https://riptutorial.com/ 386
Chapter 75: Data Synchronization with Sync
Adapter
Examples
Dummy Sync Adapter with Stub Provider
SyncAdapter
/**
* Define a sync adapter for the app.
* <p/>
* <p>This class is instantiated in {@link SyncService}, which also binds SyncAdapter to the
system.
* SyncAdapter should only be initialized in SyncService, never anywhere else.
* <p/>
* <p>The system calls onPerformSync() via an RPC call through the IBinder object supplied by
* SyncService.
*/
class SyncAdapter extends AbstractThreadedSyncAdapter {
/**
* Constructor. Obtains handle to content resolver for later use.
*/
public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
/**
* Constructor. Obtains handle to content resolver for later use.
*/
public SyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
super(context, autoInitialize, allowParallelSyncs);
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
//Jobs you want to perform in background.
Log.e("" + account.name, "Sync Start");
}
Sync Service
/**
* Define a Service that returns an IBinder for the
* sync adapter class, allowing the sync adapter framework to call
* onPerformSync().
*/
public class SyncService extends Service {
// Storage for an instance of the sync adapter
private static SyncAdapter sSyncAdapter = null;
// Object to use as a thread-safe lock
https://riptutorial.com/ 387
private static final Object sSyncAdapterLock = new Object();
/*
* Instantiate the sync adapter object.
*/
@Override
public void onCreate() {
/*
* Create the sync adapter as a singleton.
* Set the sync adapter as syncable
* Disallow parallel syncs
*/
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
}
}
}
/**
* Return an object that allows the system to invoke
* the sync adapter.
*/
@Override
public IBinder onBind(Intent intent) {
/*
* Get the object that allows external processes
* to call onPerformSync(). The object is created
* in the base class code when the SyncAdapter
* constructors call super()
*/
return sSyncAdapter.getSyncAdapterBinder();
}
}
Authenticator
https://riptutorial.com/ 388
// Ignore attempts to confirm credentials
@Override
public Bundle confirmCredentials(
AccountAuthenticatorResponse r,
Account account,
Bundle bundle) throws NetworkErrorException {
return null;
}
Authenticator Service
/**
* A bound Service that instantiates the authenticator
* when started.
*/
public class AuthenticatorService extends Service {
// Instance field that stores the authenticator object
private Authenticator mAuthenticator;
@Override
public void onCreate() {
// Create a new authenticator object
mAuthenticator = new Authenticator(this);
}
/*
* When the system binds to this Service to make the RPC call
https://riptutorial.com/ 389
* return the authenticator's IBinder.
*/
@Override
public IBinder onBind(Intent intent) {
return mAuthenticator.getIBinder();
}
}
AndroidManifest.xml additions
<service
android:name=".syncAdapter.SyncService"
android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>
<service android:name=".authenticator.AuthenticatorService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
<provider
android:name=".provider.StubProvider"
android:authorities="com.yourpackage.provider"
android:exported="false"
android:syncable="true" />
res/xml/authenticator.xml
res/xml/syncadapter.xml
https://riptutorial.com/ 390
android:userVisible="false" />
StubProvider
/*
* Define an implementation of ContentProvider that stubs out
* all methods
*/
public class StubProvider extends ContentProvider {
/*
* Always return true, indicating that the
* provider loaded correctly.
*/
@Override
public boolean onCreate() {
return true;
}
/*
* Return no type for MIME type
*/
@Override
public String getType(Uri uri) {
return null;
}
/*
* query() always returns no results
*
*/
@Override
public Cursor query(
Uri uri,
String[] projection,
String selection,
String[] selectionArgs,
String sortOrder) {
return null;
}
/*
* insert() always returns null (no URI)
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
/*
* delete() always returns "no rows affected" (0)
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
/*
* update() always returns "no rows affected" (0)
*/
public int update(
https://riptutorial.com/ 391
Uri uri,
ContentValues values,
String selection,
String[] selectionArgs) {
return 0;
}
}
Call this function on successful login to create an account with the logged-in user ID
Forcing a Sync
https://riptutorial.com/ 392
Chapter 76: Date and Time Pickers
Examples
Material DatePicker
add below dependencies to build.gradle file in dependency section. (this is an unOfficial library for
date picker)
compile 'com.wdullaer:materialdatetimepicker:2.3.0'
<Button
android:id="@+id/dialog_bt_date"
android:layout_below="@+id/resetButton"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:textColor="#FF000000"
android:gravity="center"
android:text="DATE"/>
Button button;
Calendar calendar ;
DatePickerDialog datePickerDialog ;
int Year, Month, Day ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
calendar = Calendar.getInstance();
Year = calendar.get(Calendar.YEAR) ;
Month = calendar.get(Calendar.MONTH);
Day = calendar.get(Calendar.DAY_OF_MONTH);
https://riptutorial.com/ 393
Month, Day);
datePickerDialog.setThemeDark(false);
datePickerDialog.showYearPickerFirst(false);
datePickerDialog.setAccentColor(Color.parseColor("#0072BA"));
datePickerDialog.show(getFragmentManager(), "DatePickerDialog");
}
});
}
@Override
public void onDateSet(DatePickerDialog view, int Year, int Month, int Day) {
String date = "Selected Date : " + Day + "-" + Month + "-" + Year;
Output :
https://riptutorial.com/ 394
Date Picker Dialog
It is a dialog which prompts user to select date using DatePicker. The dialog requires context, initial
year, month and day to show the dialog with starting date. When the user selects the date it
callbacks via DatePickerDialog.OnDateSetListener.
https://riptutorial.com/ 395
{
DatePickerDialog datePickerDialog = new DatePickerDialog(context,
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker datepicker,int year ,int month, int day)
{
//this condition is necessary to work properly on all android versions
if(view.isShown()){
//You now have the selected year, month and day
}
}
}, initialYear, initialMonth , initialDay);
Please note that month is a int starting from 0 for January to 11 for December
https://riptutorial.com/ 396
Chapter 77: DayNight Theme (AppCompat
v23.2 / API 14+)
Examples
Adding the DayNight theme to an app
The DayNight theme gives an app the cool capability of switching color schemes based on the
time of day and the device's last known location.
The themes you can extend from to add day night theme switching capability are the following:
• "Theme.AppCompat.DayNight"
• "Theme.AppCompat.DayNight.NoActionBar"
• "Theme.AppCompat.DayNight.DarkActionBar"
Apart from colorPrimary, colorPrimaryDark and colorAccent, you can also add any other colors that
you would like to be switched, e.g. textColorPrimary or textColorSecondary. You can add your app's
custom colors to this style as well.
For theme switching to work, you need to define a default colors.xml in the res/values directory
and another colors.xml in the res/values-night directory and define day/night colors appropriately.
To switch the theme, call the AppCompatDelegate.setDefaultNightMode(int) method from your Java
code. (This will change the color scheme for the whole app, not just any one activity or fragment.)
For example:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
You can pass any of the following three according to your choice:
• AppCompatDelegate.MODE_NIGHT_NO:this sets the default theme for your app and takes the colors
defined in the res/values directory. It is recommended to use light colors for this theme.
• AppCompatDelegate.MODE_NIGHT_YES: this sets a night theme for your app and takes the colors
defined in the res/values-night directory. It is recommended to use dark colors for this
theme.
• AppCompatDelegate.MODE_NIGHT_AUTO: this auto switches the colors of the app based on the time
of the day and the colors you have defined in values and values-night directories.
https://riptutorial.com/ 397
It is also possible to get the current night mode status using the getDefaultNightMode() method. For
example:
Please note, however, that the theme switch will not persist if you kill the app and reopen it. If you
do that, the theme will switch back to AppCompatDelegate.MODE_NIGHT_AUTO, which is the default value.
If you want the theme switch to persist, make sure you store the value in shared preferences and
load the stored value each time the app is opened after it has been destroyed.
https://riptutorial.com/ 398
Chapter 78: Define step value (increment) for
custom RangeSeekBar
Introduction
A customization of the Android RangeSeekBar proposed by Alex Florescu at
https://github.com/anothem/android-range-seek-bar
It allows to define a step value (increment), when moving the seek bar
Remarks
1- Add the increment attribute in attrs.xml
3- Init the increment value in private void init(Context context, AttributeSet attrs)
if (attrs == null)
increment = DEFAULT_INCREMENT;
else
increment = a.getInt(R.styleable.RangeSeekBar_increment, DEFAULT_INCREMENT);
4- Define the increment value in protected synchronized void onDraw(@NonNull Canvas canvas)
• minText = valueToString(getSelectedMinValue());
• maxText = valueToString(getSelectedMaxValue());
x = (int) ((getSelectedMinValue().intValue()+increment)/increment);
x = x*increment;
if (x<absoluteMaxValue.intValue())
minText = ""+x;
else
minText=""+(absoluteMaxValue.intValue()-increment);
x = (int) ((getSelectedMaxValue().intValue()+increment)/increment);
x = x*increment;
maxText = ""+x;
https://riptutorial.com/ 399
5 - Now you just have to use it. Hope it helps
Examples
Define a step value of 7
<RangeSeekBar
android:id="@+id/barPrice"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:barHeight="0.2dp"
app:barHeight2="4dp"
app:increment="7"
app:showLabels="false" />
https://riptutorial.com/ 400
Chapter 79: Design Patterns
Introduction
Design patterns are formalized best practices that the programmer can use to solve common
problems when designing an application or system.
Design patterns can speed up the development process by providing tested, proven development
paradigms.
Reusing design patterns helps to prevent subtle issues that can cause major problems, and it also
improves code readability for coders and architects who are familiar with the patterns.
Examples
Singleton Class Example
To implement Singleton pattern, we have different approaches but all of them have following
common concepts.
/**
* Singleton class.
*/
public final class Singleton {
/**
* Private constructor so nobody can instantiate the class.
*/
private Singleton() {}
/**
* Static to class instance of the class.
*/
private static final Singleton INSTANCE = new Singleton();
/**
* To be called by user to obtain instance of the class.
*
* @return instance of the singleton.
*/
public static Singleton getInstance() {
return INSTANCE;
}
}
https://riptutorial.com/ 401
Observer pattern
The observer pattern is a common pattern, which is widely used in many contexts. A real example
can be taken from YouTube: When you like a channel and want to get all news and watch new
videos from this channel, you have to subscribe to that channel. Then, whenever this channel
publishes any news, you (and all other subscribers) will receive a notification.
An observer will have two components. One is a broadcaster (channel) and the other is a receiver
(you or any other subscriber). The broadcaster will handle all receiver instances that subscribed to
it. When the broadcaster fires a new event, it will announce this to all receiver instances. When the
receiver receives an event, it will have to react to that event, for example, by turning on YouTube
and playing the new video.
class Channel{
private List<Subscriber> subscribers;
public void subscribe(Subscriber sub) {
// Add new subscriber.
}
public void unsubscribe(Subscriber sub) {
// Remove subscriber.
}
public void newEvent() {
// Notification event for all subscribers.
}
}
2. The receiver needs to implement a method that handles the event from the broadcaster:
interface Subscriber {
void doSubscribe(Channel channel);
void doUnsubscribe(Channel channel);
void handleEvent(); // Process the new event.
}
https://riptutorial.com/ 402
Chapter 80: Detect Shake Event in Android
Examples
Shake Detector in Android Example
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// ignore
}
@Override
public void onSensorChanged(SensorEvent event) {
if (mListener != null) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
float gX = x / SensorManager.GRAVITY_EARTH;
float gY = y / SensorManager.GRAVITY_EARTH;
float gZ = z / SensorManager.GRAVITY_EARTH;
https://riptutorial.com/ 403
mShakeTimestamp = now;
mShakeCount++;
mListener.onShake(mShakeCount);
}
}
}
}
Seismic is an Android device shake detection library by Square. To use it just start listening to the
shake events emitted by it.
@Override
protected void onCreate(Bundle savedInstanceState) {
sm = (SensorManager) getSystemService(SENSOR_SERVICE);
sd = new ShakeDetector(() -> { /* react to detected shake */ });
}
@Override
protected void onResume() {
sd.start(sm);
}
@Override
protected void onPause() {
sd.stop();
}
Installation
compile 'com.squareup:seismic:1.0.2'
https://riptutorial.com/ 404
Chapter 81: Device Display Metrics
Examples
Get the screens pixel dimensions
To retreive the screens width and height in pixels, we can make use of the WindowManagers
display metrics.
These DisplayMetrics hold a series of information about the devices screen, like its density or size:
To get the screens density, we also can make use of the Windowmanagers DisplayMetrics. This is
a quick example:
DP to Pixel:
Pixel to DP:
https://riptutorial.com/ 405
Chapter 82: Dialog
Parameters
Line Description
Remarks
• The dialog in the first example(Dialog) does not need to call show() when it is created as it is
handled in the constructor
• Alert Dialogs must be constructed through a new instance of the AlertDialog.Builder() class.
Following the Builder Pattern, all members of the AlertDialog.Builder can be method chained
to 'build up' the dialog instance.
• The Alert Dialog builder can directly show() the dialog -- you do not need to call create() then
show() on the AlertDialog instance
Examples
Alert Dialog
alertDialogBuilder.setTitle("Title Dialog");
alertDialogBuilder
.setMessage("Message Dialog")
.setCancelable(true)
.setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
}
})
.setNegativeButton("No",
new DialogInterface.OnClickListener() {
https://riptutorial.com/ 406
// Handle Negative Button
dialog.cancel();
}
});
https://riptutorial.com/ 407
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<DatePicker
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/datePicker"
android:layout_gravity="center_horizontal"
android:calendarViewShown="false"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ACCEPT"
android:id="@+id/buttonAccept" />
</LinearLayout>
Dialog Class:
public ChooseDate(){}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.dialog_year_picker, container);
getDialog().setTitle(getResources().getString("TITLE"));
if (isDateSetted) {
datePicker.updateDate(year, month, day);
}
return rootView;
}
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.buttonAccept:
https://riptutorial.com/ 408
int year = datePicker.getYear();
int month = datePicker.getMonth() + 1; // months start in 0
int day = datePicker.getDayOfMonth();
@Override
public void onAttach(Context context) {
super.onAttach(context);
listener = (DateListener) context;
}
this.year = year;
this.month = month;
this.day = day;
this.isDateSetted = true;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
@Override
onDateSelected(int year, int month, int day){
this.day = day;
this.month = month;
this.year = year;
}
}
DatePickerDialog
https://riptutorial.com/ 409
DatePickerDialogis the simplest way to use DatePicker, because you can show dialog anywhere in
your app. You don't have to implement your own layout with DatePicker widget.
You can get DataPicker widget from dialog above, to get access to more functions, and for
example set minimum date in milliseconds:
DatePicker
DatePicker allows user to pick date. When we create new instance of DatePicker, we can set initial
date. If we don't set initial date, current date will be set by default.
We can show DatePicker to user by using DatePickerDialog or by creating our own layout with
DatePicker widget.
//In this case user can pick date only from future
datePicker.setMinDate(System.currentTimeMillis());
//In this case user can pick date only, before following week.
datePicker.setMaxDate(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(7));
To receive information, about which date was picked by user, we have to use Listener.
If we are using DatePickerDialog, we can set OnDateSetListener in constructor when we are creating
new instance of DatePickerDialog:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
https://riptutorial.com/ 410
...
}
@Override
public void onDateSet(DatePicker datePicker, int year, int month, int day) {
}
}
Otherwise, if we are creating our own layout with DatePicker widget, we also have to create our
own listener as it was shown in other example
AlertDialog is a subclass of Dialog that can display one, two or three buttons. If you only want to
display a String in this dialog box, use the setMessage() method.
The AlertDialog from android.app package displays differently on different Android OS Versions.
The Android V7 Appcompat library provides an AlertDialog implementation which will display with
Material Design on all supported Android OS versions, as shown below:
First you need to add the V7 Appcompat library to your project. you can do this in the app level
build.gradle file:
dependencies {
compile 'com.android.support:appcompat-v7:24.2.1'
//........
}
https://riptutorial.com/ 411
import android.support.v7.app.AlertDialog;
ListView in AlertDialog
We can always use ListView or RecyclerView for selection from list of items, but if we have small
amount of choices and among those choices we want user to select one, we can use
AlertDialog.Builder setAdapter.
Perhaps, if we don't need any particular ListView, we can use a basic way:
https://riptutorial.com/ 412
void alertDialogDemo() {
// get alert_dialog.xml view
LayoutInflater li = LayoutInflater.from(getApplicationContext());
View promptsView = li.inflate(R.layout.alert_dialog, null);
// show it
alertDialog.show();
}
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Type Your Message : "
android:textAppearance="?android:attr/textAppearanceLarge" />
<EditText
android:id="@+id/etUserInput"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<requestFocus />
</EditText>
https://riptutorial.com/ 413
Fullscreen Custom Dialog with no background and no title
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</RelativeLayout>
Then in java file you can use it for an Activity or Dialog etc:
import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//You can set no content for the activity.
Dialog mDialog = new Dialog(this, R.style.AppBaseTheme);
mDialog.setContentView(R.layout.fullscreen);
mDialog.show();
}
}
The setCustomTitle() method of AlertDialog.Builder lets you specify an arbitrary view to be used
for the dialog title. One common use for this method is to build an alert dialog that has a long title.
https://riptutorial.com/ 414
builder.setCustomTitle(inflate(context, R.layout.my_dialog_title, null))
.setView(inflate(context, R.layout.my_dialog, null))
.setPositiveButton("OK", null);
my_dialog_title.xml:
<TextView
style="@android:style/TextAppearance.Small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur
tincidunt condimentum tristique. Vestibulum ante ante, pretium porttitor
iaculis vitae, congue ut sem. Curabitur ac feugiat ligula. Nulla
tincidunt est eu sapien iaculis rhoncus. Mauris eu risus sed justo
pharetra semper faucibus vel velit."
android:textStyle="bold"/>
</LinearLayout>
my_dialog.xml:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:scrollbars="vertical">
<TextView
style="@android:style/TextAppearance.Small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:text="Hello world!"/>
<TextView
style="@android:style/TextAppearance.Small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:text="Hello world again!"/>
<TextView
style="@android:style/TextAppearance.Small"
https://riptutorial.com/ 415
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:text="Hello world again!"/>
<TextView
style="@android:style/TextAppearance.Small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:text="Hello world again!"/>
</LinearLayout>
</ScrollView>
https://riptutorial.com/ 416
Chapter 83: Displaying Google Ads
Examples
Basic Ad Setup
compile 'com.google.firebase:firebase-ads:10.2.1'
<string name="banner_ad_unit_id">ca-app-pub-####/####</string>
Next place an adview wherever you want it and style it just like any other view.
<com.google.android.gms.ads.AdView
android:id="@+id/adView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
ads:adSize="BANNER"
ads:adUnitId="@string/banner_ad_unit_id">
</com.google.android.gms.ads.AdView>
MobileAds.initialize(getApplicationContext(), "ca-app-pub-YOUR_ID");
AdView mAdView = (AdView) findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder().build();
mAdView.loadAd(adRequest);
If you copy-pasted exactly you should now have a small banner ad. Simply place more AdViews
wherever you need them for more.
Adding Interstitial Ad
Interstitial ads are full-screen ads that cover the interface of their host app. They're typically
displayed at natural transition points in the flow of an app, such as between activities or during the
pause between levels in a game.
https://riptutorial.com/ 417
1. Go to your AdMob account.
5. Once the ad unit is created, you can notice the Ad unit ID on the dashboard. For example:
ca-app-pub-00000000000/000000000
6. Add dependencies
compile 'com.google.firebase:firebase-ads:10.2.1'
<string name="interstitial_full_screen">ca-app-pub-00000000/00000000</string>
<activity
android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScree
android:theme="@android:style/Theme.Translucent" />
and
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
Activity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
https://riptutorial.com/ 418
// set the ad unit ID
mInterstitialAd.setAdUnitId(getString(R.string.interstitial_full_screen));
mInterstitialAd.setAdListener(new AdListener() {
public void onAdLoaded() {
showInterstitial();
}
});
}
https://riptutorial.com/ 419
Chapter 84: Doze Mode
Remarks
Doze Mode is a set of changes and rules that put your phone to sleep when idle.
On Android 6.0 Marshmallow: Doze mode gets activated after a while the screen is off, the device
is stationary and it's running on battery.
As you can see in the diagram above, when Doze Mode gets activated, the device doesn't get any
wakelocks, network access, jobs/syncs, Alarms, GPS/Wi-fi scans.
On Android 7.0 Nougat: Imagine if your phone is on your pocket (the screen is off, it's running on
battery, but it's not stationary) you might want to get the Doze Mode features as well, right? So
that's why Google announced the Extended Doze Mode: It runs when the screen is off, but not
stationary.
https://riptutorial.com/ 420
As you can see in this diagram, only Network Access and jobs/syncs are disabled. Note that the
Extended Doze doesn't replace the first Doze Mode. They work together, depending on the phone
state (stationary or not). Here are the distinctions:
https://riptutorial.com/ 421
Developers should be aware that:
• Doze might keep temporary wakelock and network access for High-priority GCM (Google
Cloud Messaging) messages (for cases wwhere the user needs an immediate notification);
• Foreground services (such as a music playback) will continue to work.
Examples
Exclude app from using doze mode
https://riptutorial.com/ 422
Now this app will show under not optimized apps.
Whitelisting won't disable the doze mode for your app, but you can do that by using network and
hold-wake locks.
The result of starting the activity above can be verfied by the following code:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == MY_IGNORE_OPTIMIZATION_REQUEST) {
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
boolean isIgnoringBatteryOptimizations =
pm.isIgnoringBatteryOptimizations(getPackageName());
if(isIgnoringBatteryOptimizations){
// Ignoring battery optimization
}else{
// Not ignoring battery optimization
}
}
}
https://riptutorial.com/ 423
Chapter 85: Drawables
Examples
Tint a drawable
A drawable can be tinted a certain color. This is useful for supporting different themes within your
application, and reducing the number of drawable resource files.
Drawable d = context.getDrawable(R.drawable.ic_launcher);
d.setTint(Color.WHITE);
//NOTE: If your original drawableRes was in use somewhere (i.e. it was the result of
//a call to a `getBackground()` method then at this point you still need to replace
//the background. setTint does *not* alter the instance that drawableRes points to,
//but instead creates a new drawable instance
Please not that int color is not referring to a color Resource, however you are not limited to those
colours defined in the 'Color' class. When you have a colour defined in your XML which you want
to use you must just first get it's value.
getResources().getColor(R.color.your_color);
Or on newer targets:
ContextCompat.getColor(context, R.color.your_color);
https://riptutorial.com/ 424
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<stroke
android:width="1dp"
android:color="@android:color/white" />
</shape>
mView.setBackGround(R.drawlable.custom_rectangle);
Reference screenshot:
Circular View
For a circular View (in this case TextView) create a drawble round_view.xml in drawble folder:
<TextView
android:id="@+id/game_score"
android:layout_width="60dp"
android:layout_height="60dp"
https://riptutorial.com/ 425
android:background="@drawable/round_score"
android:padding="6dp"
android:text="100"
android:textColor="#fff"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center" />
Custom Drawable
/**
* Public constructor for the Icon drawable
*
* @param icon pass the drawable of the icon to be drawn at the center
* @param backgroundColor background color of the shape
*/
public IconDrawable(Drawable icon, int backgroundColor) {
this.icon = icon;
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(backgroundColor);
desiredIconWidth = 50;
desiredIconHeight = 50;
}
@Override
public void draw(Canvas canvas) {
//if we are setting this drawable to a 80dpX80dp imageview
https://riptutorial.com/ 426
//getBounds will return that measurements,we can draw according to that width.
Rect bounds = getBounds();
//drawing the circle with center as origin and center distance as radius
canvas.drawCircle(bounds.centerX(), bounds.centerY(), bounds.centerX(), paint);
//set the icon drawable's bounds to the center of the shape
icon.setBounds(bounds.centerX() - (desiredIconWidth / 2), bounds.centerY() -
(desiredIconHeight / 2), (bounds.centerX() - (desiredIconWidth / 2)) + desiredIconWidth,
(bounds.centerY() - (desiredIconHeight / 2)) + desiredIconHeight);
//draw the icon to the bounds
icon.draw(canvas);
@Override
public void setAlpha(int alpha) {
//sets alpha to your whole shape
paint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
//sets color filter to your whole shape
paint.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
//give the desired opacity of the shape
return PixelFormat.TRANSLUCENT;
}
}
<ImageView
android:layout_width="80dp"
android:id="@+id/imageView"
android:layout_height="80dp" />
IconDrawable iconDrawable=new
IconDrawable(ContextCompat.getDrawable(this,android.R.drawable.ic_media_play),ContextCompat.getColor(th
imageView.setImageDrawable(iconDrawable);
Screenshot
https://riptutorial.com/ 427
Chapter 86: EditText
Examples
Working with EditTexts
The EditText is the standard text entry widget in Android apps. If the user needs to enter text into
an app, this is the primary way for them to do that.
EditText
There are many important properties that can be set to customize the behavior of an EditText.
Several of these are listed below. Check out the official text fields guide for even more input field
details.
Usage
An EditText is added to a layout with all default behaviors with the following XML:
<EditText
android:id="@+id/et_simple"
android:layout_height="wrap_content"
android:layout_width="match_parent">
</EditText>
Note that an EditText is simply a thin extension of the TextView and inherits all of the same
properties.
<EditText
android:singleLine="true"
android:lines="1"
/>
You can limit the characters that can be entered into a field using the digits attribute:
<EditText
android:inputType="number"
android:digits="01"
https://riptutorial.com/ 428
/>
This would restrict the digits entered to just "0" and "1". We might want to limit the total
number of characters with:
<EditText
android:maxLength="5"
/>
Using these properties we can define the expected input behavior for text fields.
Adjusting Colors
You can adjust the highlight background color of selected text within an EditText with the
android:textColorHighlight property:
<EditText
android:textColorHighlight="#7cff88"
/>
You may want to set the hint for the EditText control to prompt a user for specific input
with:
<EditText
...
android:hint="@string/my_hint">
</EditText>
Hints
Assuming you are using the AppCompat library, you can override the styles colorControlNormal,
colorControlActivated, and colorControlHighlight:
If you do not see these styles applied within a DialogFragment, there is a known bug when using
the LayoutInflater passed into the onCreateView() method.
The issue has already been fixed in the AppCompat v23 library. See this guide about how to
upgrade. Another temporary workaround is to use the Activity's layout inflater instead of the one
passed into the onCreateView() method:
https://riptutorial.com/ 429
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState) {
View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fragment,
container);
}
Check out the basic event listeners cliffnotes for a look at how to listen for changes to an EditText
and perform an action when those changes occur.
Traditionally, the EditText hides the hint message (explained above) after the user starts typing. In
addition, any validation error messages had to be managed manually by the developer.
With the TextInputLayout you can setup a floating label to display hints and error messages. You
can find more details here.
Text fields can have different input types, such as number, date, password, or email address. The
type determines what kind of characters are allowed inside the field, and may prompt the virtual
keyboard to optimize its layout for frequently used characters.
By default, any text contents within an EditText control is displayed as plain text. By setting the
inputType attribute, we can facilitate input of different types of information, like phone numbers and
passwords:
<EditText
...
android:inputType="phone">
</EditText>
Type Description
https://riptutorial.com/ 430
Type Description
The android:inputType also allows you to specify certain keyboard behaviors, such as whether to
capitalize all new words or use features like auto-complete and spelling suggestions.
Here are some of the common input type values that define keyboard behaviors:
Type Description
Normal text keyboard that capitalizes the first letter for each new
textCapSentences
sentence
Normal text keyboard that capitalizes every word. Good for titles or
textCapWords
person names
<EditText
android:id="@+id/postal_address"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/postal_address_hint"
android:inputType="textPostalAddress|
textCapWords|
textNoSuggestions" />
`inputype` attribute
textFilter= Keyboard: alphabet/default. Enter button: Send/Next. Emotion: yes. Case: lowercase.
Suggestion: no. Add. chars: , and . and everything
https://riptutorial.com/ 431
Sentence case. Suggestion: yes. Add. chars: , and . and everything
time= Keyboard: numeric. Enter button: Send/Next. Emotion: no. Case: -. Suggestion: no. Add.
chars: :
number= Keyboard: numeric. Enter button: Send/Next. Emotion: no. Case: -. Suggestion: no.
Add. chars: nothing
(No type)= Keyboard: alphabet/default. Enter button: nextline. Emotion: yes. Case: lowercase.
Suggestion: yes. Add. chars: , and . and everything
text= Keyboard: Keyboard: alphabet/default. Enter button: Send/Next. Emotion: yes. Case:
lowercase. Suggestion: yes. Add. chars: , and . and everything
textUri= Keyboard: alphabet/default. Enter button: Send/Next. Emotion: no. Case: lowercase.
Suggestion: no. Add. chars: / and . and everything
phone= Keyboard: numeric. Enter button: Send/Next. Emotion: no. Case: -. Suggestion: no.
Add. chars: *** # . - / () W P N , +**
Hiding SoftKeyboard
Hiding Softkeyboard is a basic requirement usually when working with EditText. The
softkeyboard by default can only be closed by pressing back button and so most developers use
InputMethodManager to force Android to hide the virtual keyboard calling
https://riptutorial.com/ 432
hideSoftInputFromWindow and passing in the token of the window containing your focused view.
The code to do the following:
The code is direct, but another major problems that arises is that the hide function needs to be
called when some event occurs. What to do when you need the Softkeyboard hidden upon
pressing anywhere other than your EditText? The following code gives a neat function that needs
to be called in your onCreate() method just once.
view.setOnTouchListener(new View.OnTouchListener() {
});
}
setupUI(innerView);
}
}
}
Icon or button inside Custom Edit Text and its action and click listeners.
This example will help to have the Edit text with the icon at the right side.
https://riptutorial.com/ 433
}
setIcon();
}
setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
setSelection(getText().length());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
final int right = getRight();
final int drawableSize = getCompoundPaddingRight();
final int x = (int) event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (x + EXTRA_TOUCH_AREA >= right - drawableSize && x <= right +
EXTRA_TOUCH_AREA) {
touchDown = true;
return true;
}
break;
case MotionEvent.ACTION_UP:
if (x + EXTRA_TOUCH_AREA >= right - drawableSize && x <= right +
EXTRA_TOUCH_AREA && touchDown) {
touchDown = false;
if (mIconClickListener != null) {
mIconClickListener.onClick();
https://riptutorial.com/ 434
}
return true;
}
touchDown = false;
break;
}
return super.onTouchEvent(event);
}
}
If you want to change the touch area you can change the EXTRA_TOUCH_AREA
values default I gave as 50.
And for Enable the button and click listener you can call from your Activity or Fragment like this,
}
});
https://riptutorial.com/ 435
Chapter 87: Email Validation
Examples
Email address validation
Add the following method to check whether an email address is valid or not:
The above method can easily be verified by converting the text of an EditText widget into a String:
if(isValidEmailId(edtEmailId.getText().toString().trim())){
Toast.makeText(getApplicationContext(), "Valid Email Address.", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(getApplicationContext(), "InValid Email Address.",
Toast.LENGTH_SHORT).show();
}
if (Patterns.EMAIL_ADDRESS.matcher(email).matches()){
Log.i("EmailCheck","It is valid");
}
https://riptutorial.com/ 436
Chapter 88: Emulator
Remarks
AVD stands for Android Virtual Device
Examples
Taking screenshots
If you want to take a screenshot from the Android Emulator (2.0), then you just need to press Ctrl
+ S or you click on the camera icon on the side bar:
https://riptutorial.com/ 437
https://riptutorial.com/ 438
2. A drop shadow below the device frame.
3. A screen glare across device frame and screenshot.
https://riptutorial.com/ 439
https://riptutorial.com/ 440
.
You can also access AVD Manager from Android studio using Tools > Android > AVD Manager or by
clicking on the AVD Manager icon in the toolbar which is the second in the screenshot below.
Simulate call
To simulate a phone call, press the 'Extended controls' button indicated by three dots, choose
'Phone' and select 'Call'. You can also optionally change the phone number.
https://riptutorial.com/ 441
First of all, ensure that you've enabled the 'Virtualization' in your BIOS setup.
Start the Android SDK Manager, select Extras and then select Intel Hardware Accelerated
Execution Manager and wait until your download completes. If it still doesn't work, open your
SDK folder and run /extras/intel/Hardware_Accelerated_Execution_Manager/IntelHAXM.exe.
If your CPU does not support VT-x or SVM, you can not use x86-based Android
images. Please use ARM-based images instead.
After installation completed, confirm that the virtualization driver is operating correctly by opening a
command prompt window and running the following command: sc query intelhaxm
To run an x86-based emulator with VM acceleration: If you are running the emulator from the
command line, just specify an x86-based AVD: emulator -avd <avd_name>
If you follow all the steps mentioned above correctly, then surely you should be able to see your
AVD with HAXM coming up normally.
https://riptutorial.com/ 442
Chapter 89: Enhancing Alert Dialogs
Introduction
This topic is about enhancing an AlertDialog with additional features.
Examples
Alert dialog containing a clickable link
In order to show an alert dialog containing a link which can be opened by clicking it, you can use
the following code:
builder1.setCancelable(false);
builder1.setPositiveButton("ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
https://riptutorial.com/ 443
Chapter 90: Enhancing Android Performance
Using Icon Fonts
Remarks
Icon Fonts are like normal font types that have symbols instead of letters. It can be used in your
application with at-most ease.
They are:
• Flexible
• Scalable
• Vectors
• Fast Processable
• Light Weight
• Accessible
Effect on Size
Exporting an image in various sizes for android devices would cost your app, additional asset size
of around 30kB per image. While adding a font file(.ttf) with around 36 icons would cost just 9kB.
Just imagine the case if you are adding 36 individual files of various configurations it would be
around 1000kB. It’s a reasonable amount of space that you will save by using icon fonts.
• Icon fonts can be used in navigation drawer. Using them in navigation views as icon of menu
items is not possible as the menu file cannot be created without specifying the title. So it is
advisable to use svg files as resources for these icons.
• Icon fonts cannot be used in floating action button. as they does not have a setText()
attribute.
• External fonts cannot be applied from xml. They must be specified using the java file. Or else
you need to extend the basic view and create a view as specified in this post
Examples
How to integrate Icon fonts
You may create your font icon file from online websites such as icomoon, where you can
upload SVG files of the required icons and then download the created icon font. Then, place
https://riptutorial.com/ 444
the .ttf font file into a folder named fonts (name it as you wish) in the assets folder:
Now, create the following helper class, so that you can avoid repeating the initialisation code
for the font:
You may use the Typeface class in order to pick the font from the assets. This way you can
set the typeface to various views, for example, to a button:
Now, the button typeface has been changed to the newly created icon font.
Open the styles.css file attached to the icon font. There you will find the styles with Unicode
characters of your icons:
.icon-arrow-circle-down:before {
content: “\e001”;
}
.icon-arrow-circle-left:before {
content: “\e002”;
}
https://riptutorial.com/ 445
.icon-arrow-circle-o-down:before {
content: “\e003”;
}
.icon-arrow-circle-o-left:before {
content: “\e004”;
}
This resource file will serve as a dictionary, which maps the Unicode character associated
with a specific icon to a human-readable name. Now, create the string resources as follows:
<resources>
<! — Icon Fonts -->
<string name=”icon_arrow_circle_down”> </string>
<string name=”icon_arrow_circle_left”> </string>
<string name=”icon_arrow_circle-o_down”> </string>
<string name=”icon_arrow_circle_o_left”> </string>
</resources>
Now, you may use your font in various views, for example, as follows:
button.setText(getString(R.string.icon_arrow_circle_left))
You may also create button text views using icon fonts:
https://riptutorial.com/ 446
TabLayout with icon fonts
CustomTypefaceSpan fonte;
List<Fragment> fragments = new ArrayList<>(4);
private String[] icons = {"\ue001","\uE002","\uE003","\uE004"};
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
https://riptutorial.com/ 447
@Override
public CharSequence getPageTitle(int position) {
SpannableStringBuilder ss = new SpannableStringBuilder(icons[position]);
ss.setSpan(fonte,0,ss.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
ss.setSpan(new RelativeSizeSpan(1.5f),0,ss.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE );
return ss;
}
@Override
public int getCount() {
return 4;
}
//..
TabLayout tabs;
ViewPager tabs_pager;
public CustomTypefaceSpan fonte;
//..
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
fm = getSupportFragmentManager();
fonte = new
CustomTypefaceSpan("icomoon",Typeface.createFromAsset(getAssets(),"myfont.ttf"));
this.tabs = ((TabLayout) hasViews.findViewById(R.id.tabs));
this.tabs_pager = ((ViewPager) hasViews.findViewById(R.id.tabs_pager));
//...
}
@Override
protected void onStart() {
super.onStart();
//..
tabs_pager.setAdapter(new TabAdapter(fm,fonte));
tabs.setupWithViewPager(tabs_pager);
//..
https://riptutorial.com/ 448
Chapter 91: Exceptions
Examples
NetworkOnMainThreadException
This is only thrown for applications targeting the Honeycomb SDK or higher.
Applications targeting earlier SDK versions are allowed to do networking on their main
event loop threads, but it's heavily discouraged.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Above code will throw NetworkOnMainThreadException for applications targeting Honeycomb SDK
(Android v3.0) or higher as the application is trying to perform a network operation on the main
thread.
https://riptutorial.com/ 449
To avoid this exception, your network operations must always run in a background task via an
AsyncTask, Thread, IntentService, etc.
@Override
protected Void doInBackground(String[] params) {
Uri.Builder builder = new Uri.Builder().scheme("http").authority("www.google.com");
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
URL url;
try {
url = new URL(builder.build().toString());
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
} catch (IOException e) {
Log.e("TAG","Connection error", e);
} finally{
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e("TAG", "Error closing stream", e);
}
}
}
return null;
}
}
ActivityNotFoundException
This is a very common Exception. It causes your application to stop during the start or execution of
your app. In the LogCat you see the message:
In this case, check if you have declared your activity in the AndroidManifest.xml file.
OutOfMemoryError
This is a runtime error that happens when you request a large amount of memory on the heap.
This is common when loading a Bitmap into an ImageView.
https://riptutorial.com/ 450
You have some options:
Add the "largeHeap" option to the application tag in your AndroidManifest.xml. This will make more
memory available to your app but will likely not fix the root issue.
Avoid loading the entire bitmap into memory at once by sampling a reduced size, using
BitmapOptions and inSampleSize.
DexException
This error occurs because the app, when packaging, finds two .dex files that define the same set
of methods.
Usually this happens because the app has accidentally acquired 2 separate dependencies on the
same library.
For instance, say you have a project, and you want to rely on two libraries: A and B, which each
have their own dependencies. If library B already has a dependency on library A, this error will be
thrown if library A is added to the project by itself. Compiling library B already gave the set of code
from A, so when the compiler goes to bundle library A, it finds library A's methods already
packaged.
To resolve, make sure that none of your dependencies could accidentally be added twice in such
a manner
UncaughtException
If you want to handle uncaught exceptions try to catch them all in onCreate method of you
Application class:
https://riptutorial.com/ 451
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
try {
Thread
.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable e) {
Log.e(TAG,
"Uncaught Exception thread: "+thread.getName()+"
"+e.getStackTrace());
handleUncaughtException (thread, e);
}
});
} catch (SecurityException e) {
Log.e(TAG,
"Could not set the Default Uncaught Exception Handler:"
+e.getStackTrace());
}
}
This is how you can react to exceptions which have not been catched, similar to the system's
standard "Application XYZ has crashed"
import android.app.Application;
import android.util.Log;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* Application class writing unexpected exceptions to a crash file before crashing.
*/
public class MyApplication extends Application {
private static final String TAG = "ExceptionHandler";
@Override
public void onCreate() {
super.onCreate();
https://riptutorial.com/ 452
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable e) {
try {
handleUncaughtException(e);
System.exit(1);
} catch (Throwable e2) {
Log.e(TAG, "Exception in custom exception handler", e2);
defaultHandler.uncaughtException(thread, e);
}
}
});
}
// You can (and probably should) also display a dialog to notify the user
}
}
https://riptutorial.com/ 453
Chapter 92: ExoPlayer
Examples
Add ExoPlayer to the project
Via jCenter
compile 'com.google.android.exoplayer:exoplayer:rX.X.X'
where rX.X.X is the your preferred version. For the latest version, see the project's Releases. For
more details, see the project on Bintray.
Using ExoPlayer
Now you have to create a DataSource. When you want to stream mp3 you can use the
DefaultUriDataSource. You have to pass the Context and a UserAgent. To keep it simple play a
local file and pass null as userAgent:
uri points to your file, as an Extractor you can use a simple default Mp3Extractor if you want to
play mp3. requestedBufferSize can be tweaked again according to your requirements. Use 5000
for example.
Now you can create your audio track renderer using the sample source as follows:
https://riptutorial.com/ 454
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource);
exoPlayer.prepare(audioRenderer);
exoPlayer.setPlayWhenReady(true);
Main steps to play video & audio using the standard TrackRenderer
implementations
https://riptutorial.com/ 455
Chapter 93: Facebook SDK for Android
Syntax
• newInstance : To create single instance of Facebook helper class.
• loginUser : To login user.
• signOut : To log out user.
• getCallbackManager : To get callback for Facebook.
• getLoginCallback : To get callback for Login.
• getKeyHash : To generate Facebook Key Hash.
Parameters
Parameter Details
Activity A context
Examples
How to add Facebook Login in Android
// Facebook login
compile 'com.facebook.android:facebook-android-sdk:4.21.1'
/**
* Created by Andy
* An utility for Facebook
*/
public class FacebookSignInHelper {
private static final String TAG = FacebookSignInHelper.class.getSimpleName();
private static FacebookSignInHelper facebookSignInHelper = null;
https://riptutorial.com/ 456
private CallbackManager callbackManager;
private Activity mActivity;
private static final Collection<String> PERMISSION_LOGIN = (Collection<String>)
Arrays.asList("public_profile", "user_friends","email");
private FacebookCallback<LoginResult> loginCallback;
@Override
public void onCancel() {
Log.d(TAG, "Facebook: Cancelled by user");
}
@Override
public void onError(FacebookException error) {
Log.d(TAG, "FacebookException: " + error.getMessage());
}
};
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* To login user on facebook without default Facebook button
*/
public void loginUser() {
try {
LoginManager.getInstance().registerCallback(callbackManager, loginCallback);
LoginManager.getInstance().logInWithReadPermissions(this.mActivity,
PERMISSION_LOGIN);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* To log out user from facebook
*/
public void signOut() {
https://riptutorial.com/ 457
// Facebook sign out
LoginManager.getInstance().logOut();
}
/**
* Attempts to log debug key hash for facebook
*
* @param context : A reference to context
* @return : A facebook debug key hash
*/
public static String getKeyHash(Context context) {
String keyHash = null;
try {
PackageInfo info = context.getPackageManager().getPackageInfo(
context.getPackageName(),
PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
keyHash = Base64.encodeToString(md.digest(), Base64.DEFAULT);
Log.d(TAG, "KeyHash:" + keyHash);
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return keyHash;
}
}
FacebookSignInHelper facebookSignInHelper =
FacebookSignInHelper.newInstance(LoginActivity.this, fireBaseAuthHelper);
facebookSignInHelper.loginUser();
If you want to retrieve the details of a user's Facebook profile, you need to set permissions for the
same:
https://riptutorial.com/ 458
loginButton = (LoginButton)findViewById(R.id.login_button);
loginButton.setReadPermissions(Arrays.asList("email", "user_about_me"));
You can keep adding more permissions like friends-list, posts, photos etc. Just pick the right
permission and add it the above list.
Note: You don't need to set any explicit permissions for accessing the public profile (first name,
last name, id, gender etc).
Once you first add the Facebook login/signup, the button looks something like:
Most of the times, it doesn't match with the design-specs of your app. And here's how you can
customize it:
<FrameLayout
android:layout_below="@+id/no_network_bar"
android:id="@+id/FrameLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.facebook.login.widget.LoginButton
android:id="@+id/login_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone" />
<Button
android:background="#3B5998"
android:layout_width="match_parent"
android:layout_height="60dp"
android:id="@+id/fb"
android:onClick="onClickFacebookButton"
android:textAllCaps="false"
android:text="Sign up with Facebook"
android:textSize="22sp"
android:textColor="#ffffff" />
</FrameLayout>
Just wrap the original com.facebook.login.widget.LoginButton into a FrameLayout and make its
visibility gone.
Next, add your custom button in the same FrameLayout. I've added some sample specs. You can
always make your own drawable background for the facebook button and set it as the background
of the button.
The final thing we do is simply convert the click on my custom button to a click on the facecbook
https://riptutorial.com/ 459
button:
<activity
android:name="com.facebook.FacebookActivity"
android:configChanges= "keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:label="@string/app_name" />
<com.facebook.login.widget.LoginButton
android:id="@+id/login_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
4. Now you have the Facebook button. If the user clicks on it, the Facebook login dialog will
come up on top of the app's screen. Here the user can fill in their credentials and press the
Log In button. If the credentials are correct, the dialog grants the corresponding permissions
and a callback is sent to your original activity containing the button. The following code
shows how you can receive that callback:
@Override
public void onCancel() {
https://riptutorial.com/ 460
// The user either cancelled the Facebook login process or didn't authorize the
app.
}
@Override
public void onError(FacebookException exception) {
// The dialog was closed with an error. The exception will help you recognize
what exactly went wrong.
}
});
com.facebook.login.LoginManager.getInstance().logOut();
For versions before 4.0, the logging out is gone by explicitly clearing the access token:
https://riptutorial.com/ 461
Chapter 94: Fast way to setup Retrolambda
on an android project.
Introduction
Retrolambda is a library which allows to use Java 8 lambda expressions, method references and
try-with-resources statements on Java 7, 6 or 5.
The Gradle Retrolambda Plug-in allows to integrate Retrolambda into a Gradle based build. This
allows for example to use these constructs in an Android application, as standard Android
development currently does not yet support Java 8.
Examples
Setup and example how to use:
Setup Steps:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'me.tatarka:gradle-retrolambda:3.2.3'
}
}
4. Add these lines to your application module’s build.gradle to inform the IDE of the language
level:
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
https://riptutorial.com/ 462
Example:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
log("Clicked");
}
});
Become this:
https://riptutorial.com/ 463
Chapter 95: Fastjson
Introduction
Fastjson is a Java library that can be used to convert Java Objects into their JSON
representation. It can also be used to convert a JSON string to an equivalent Java object.
Fastjson Features:
Provide simple toJSONString() and parseObject() methods to convert Java objects to JSON and
vice-versa
Syntax
• Object parse(String text)
• JSONObject parseObject(String text)
• T parseObject(String text, Class<T> clazz)
• JSONArray parseArray(String text)
• <T> List<T> parseArray(String text, Class<T> clazz)
• String toJSONString(Object object)
• String toJSONString(Object object, boolean prettyFormat)
• Object toJSON(Object javaObject)
Examples
Parsing JSON with Fastjson
Encode
import com.alibaba.fastjson.JSON;
https://riptutorial.com/ 464
rootUser.setId(3L);
rootUser.setName("root");
group.addUser(guestUser);
group.addUser(rootUser);
System.out.println(jsonString);
Output
{"id":0,"name":"admin","users":[{"id":2,"name":"guest"},{"id":3,"name":"root"}]}
Decode
Group.java
User.java
https://riptutorial.com/ 465
public class User {
Code
Output
{1:"No.1",2:"No.2",3:[{"id":2,"name":"Liu"},{"id":3,"name":"Yue"}]}
https://riptutorial.com/ 466
Chapter 96: fastlane
Remarks
fastlane is a tool for iOS, Mac, and Android developers to automate tedious tasks like generating
screenshots, dealing with provisioning profiles, and releasing your application.
Docs: https://docs.fastlane.tools/
Examples
Fastfile to build and upload multiple flavors to Beta by Crashlytics
This is a sample Fastfile setup for a multi-flavor app. It gives you an option to build and deploy all
flavors or a single flavor. After the deployment, it reports to Slack the status of the deployment,
and sends a notification to testers in Beta by Crashlytics testers group.
Using a single Fastlane file, you can manage iOS, Android, and Mac apps. If you are using this file
just for one app platform is not required.
How It Works
https://riptutorial.com/ 467
and throw an error. In this scenario, I capture it and report to Slack as a failure, so you will
know which app is inactive.
7. If deployment is successful, fastlane will send a success message to Slack.
8. #{/([^\/]*)$/.match(apk)} this regex is used to get flavor name from APK path. You can
remove it if it does not work for you.
9. get_version_name and get_version_code are two Fastlane plugins to retrieve app version name
and code. You have to install these gems if you want to use, or you can remove them. Read
more about Plugins here.
10. The else statement will be executed if you are building and deploying a single APK. We don't
have to provide apk_path to Crashlytics since we have only one app.
11. error do block at the end is used to get notified if anything else goes wrong during execution.
Note
Don't forget to replace SLACK_URL, API_TOKEN, GROUP_NAME and BUILD_SECRET with your own credentials.
fastlane_version "1.46.1"
default_platform :android
platform :android do
before_all do
ENV["SLACK_URL"] = "https://hooks.slack.com/servic...."
end
gradle(task: "clean")
gradle(task: "assemble",
build_type: "Release",
flavor: options[:app])
# If user calls `fastlane android beta` command, it will build all projects and push
them to Crashlytics
if options[:app].nil?
lane_context[SharedValues::GRADLE_ALL_APK_OUTPUT_PATHS].each do | apk |
begin
crashlytics(
api_token: "[API_TOKEN]",
build_secret: "[BUILD_SECRET]",
groups: "[GROUP_NAME]",
apk_path: apk,
notifications: "true"
)
slack(
message: "Successfully deployed new build for #{/([^\/]*)$/.match(apk)}
#{get_version_name} - #{get_version_code}",
success: true,
default_payloads: [:git_branch, :lane, :test_result]
https://riptutorial.com/ 468
)
rescue => ex
# If the app is inactive in Crashlytics, deployment will fail. Handle it
here and report to slack
slack(
message: "Error uploading => #{/([^\/]*)$/.match(apk)}
#{get_version_name} - #{get_version_code}: #{ex}",
success: false,
default_payloads: [:git_branch, :lane, :test_result]
)
end
end
after_all do |lane|
# This block is called, only if the executed lane was successful
slack(
message: "Operation completed for
#{lane_context[SharedValues::GRADLE_ALL_APK_OUTPUT_PATHS].size} app(s) for #{get_version_name}
- #{get_version_code}",
default_payloads: [:git_branch, :lane, :test_result],
success: true
)
end
else
# Single APK upload to Beta by Crashlytics
crashlytics(
api_token: "[API_TOKEN]",
build_secret: "[BUILD_SECRET]",
groups: "[GROUP_NAME]",
notifications: "true"
)
after_all do |lane|
# This block is called, only if the executed lane was successful
slack(
message: "Successfully deployed new build for #{options[:app]}
#{get_version_name} - #{get_version_code}",
default_payloads: [:git_branch, :lane, :test_result],
success: true
)
end
end
Fastfile lane to build and install all flavors for given build type to a device
Add this lane to your Fastfile and run fastlane installAll type:{BUILD_TYPE} in command line.
Replace BUILD_TYPE with the build type you want to build.
https://riptutorial.com/ 469
This command will build all flavors of given type and install it to your device. Currently, it doesn't
work if you have more than one device attached. Make sure you have only one. In the future I'm
planning to add option to select target device.
gradle(task: "clean")
gradle(task: "assemble",
build_type: options[:type])
lane_context[SharedValues::GRADLE_ALL_APK_OUTPUT_PATHS].each do | apk |
begin
adb(
command: "install -r #{apk}"
)
rescue => ex
puts ex
end
end
end
https://riptutorial.com/ 470
Chapter 97: FileIO with Android
Introduction
Reading and writing files in Android are not different from reading and writing files in standard
Java. Same java.io package can be used. However, there is some specific related to the folders
where you are allowed to write, permissions in general and MTP work arounds.
Remarks
Android provides means for sharing the file between multiple applications as documented here.
This is not required if there is only one app that creates and uses the file.
Android provides alternative storage options like shared and private preferences, saved bundles
and built-in database. In some cases, they are better choice than just using plain files.
Android activity does have few specific methods that look like replacements of the Java standard
File IO methods. For instance, instead for File.delete() you can call Context.deleteFile(), and
instead of applying File.listFiles() recursively you can call Context.fileList() to get the list of all
your app specific files with somewhat less code. However, they do not provide extra functionality
beyond standard java.io package.
Examples
Obtaining the working folder
You can get your working folder by calling the method getFilesDir() on your Activity (Activity is the
central class in your application that inherits from Context. See here). Reading is not different.
Only your application will have access to this folder.
There is nothing Android specific with this code. If you write lots of small values often, use
BufferedOutputStream to reduce the wear of the device internal SSD.
https://riptutorial.com/ 471
Serializing the object
The old good Java object serialization is available for you in Android. you can define Serializable
classes like:
oout.close()
Java object serialization may be either perfect or really bad choice, depending on what do you
want to do with it - outside the scope of this tutorial and sometimes opinion based. Read about the
versioning first if you decide to use it.
You can also read and write from/to memory card (SD card) that is present in many Android
devices. Files in this location can be accessed by other programs, also directly by the user after
connecting device to PC via USB cable and enabling MTP protocol.
Finding the SD card location is somewhat more problematic. The Environment class contains
static methods to get "external directories" that should normally be inside the SD card, also
information if the SD card exists at all and is writable. This question contains valuable answers
how to make sure the right location will be found.
For older versions of Android putting permissions it is enough to put these permissions into
manifest (the user must approve during installation). However starting from Android 6.0 Android
asks the user for approval at the time of the first access, and you must support this new approach.
Otherwise access is denied regardless of your manifest.
https://riptutorial.com/ 472
In Android 6.0, first you need to check for permission, then, if not granted, request it. The code
examples can be found inside this SO question.
If you create files for exporting via USB cable to desktop using MTP protocol, may be a problem
that newly created files are not immediately visible in the file explorer running on the connected
desktop PC. To to make new files visible, you need to call MediaScannerConnection:
out.close()
MediaScannerConnection.scanFile(this, new String[] {file.getPath()}, null, null);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
Uri.fromFile(file)));
This MediaScannerConnection call code works for files only, not for directories. The problem is
described in this Android bug report. This may be fixed for some version in the future, or on some
devices.
Small files are processed in a fraction of second and you can read / write them in place of the
code where you need this. However if the file is bigger or otherwise slower to process, you may
need to use AsyncTask in Android to work with the file in the background:
@Override
protected File doInBackground(String... params) {
try {
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOCUMENTS), "bigAndComplexDocument.odf");
FileOutputStream out = new FileOutputStream(file)
out.close()
return file;
} catch (IOException ex) {
Log.e("Unable to write", ex);
return null;
}
}
@Override
protected void onPostExecute(File result) {
// This is called when we finish
}
@Override
https://riptutorial.com/ 473
protected void onPreExecute() {
// This is called before we begin
}
@Override
protected void onProgressUpdate(Void... values) {
// Unlikely required for this example
}
}
}
and then
This SO question contains the complete example on how to create and call the AsyncTask. Also
see the question on error handling on how to handle IOExceptions and other errors.
https://riptutorial.com/ 474
Chapter 98: FileProvider
Examples
Sharing a file
In this example you'll learn how to share a file with other apps. We'll use a pdf file in this example
although the code works with every other format as well.
The roadmap:
1. Create a new XML file that will contain the paths, e.g. res/xml/filepaths.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="pdf_folder" path="documents/"/>
</paths>
<manifest>
...
<application>
...
<provider
android:name="android.support.v4.context.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
...
</application>
https://riptutorial.com/ 475
...
</manifest>
As you can see in the code we first make a new File class representing the file. To get a URI we
ask FileProvider to get us one. The second argument is important: it passes the authority of a
FileProvider. It must be equal to the authority of the FileProvider defined in the manifest.
Context.startActivity(intent);
A chooser is a menu from which the user can choose with which app he/she wants to share the
file. The flag Intent.FLAG_GRANT_READ_URI_PERMISSION is needed to grant temporary read
access permission to the URI.
https://riptutorial.com/ 476
Chapter 99: Fingerprint API in android
Remarks
see also
Examples
Adding the Fingerprint Scanner in Android application
To use this feature in your app, first add the USE_FINGERPRINT permission in your
manifest.
<uses-permission
android:name="android.permission.USE_FINGERPRINT" />
First you need to create a symmetric key in the Android Key Store using KeyGenerator
which can be only be used after the user has authenticated with fingerprint and pass a
KeyGenParameterSpec.
KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
keyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_SIGN)
.setDigests(KeyProperties.DIGEST_SHA256)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
.setUserAuthenticationRequired(true)
.build());
keyPairGenerator.generateKeyPair();
https://riptutorial.com/ 477
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
PrivateKey key = (PrivateKey) keyStore.getKey(KEY_NAME, null);
getContext().getSystemService(FingerprintManager.class)
onAuthenticationError
onAuthenticationHelp
onAuthenticationSucceeded
onAuthenticationFailed
To Start
fingerprintManager
.authenticate(cryptoObject, mCancellationSignal, 0 , this, null);
Cancel
android.os.CancellationSignal;
@Override
This example helper class interacts with the finger print manager and performs encryption and
decryption of password. Please note that the method used for encryption in this example is AES.
This is not the only way to encrypt and other examples exist. In this example the data is encrypted
https://riptutorial.com/ 478
and decrypted in the following manner:
Encryption:
Decryption:
@TargetApi(Build.VERSION_CODES.M)
public boolean init() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
setError("This Android version does not support fingerprint authentication");
return false;
https://riptutorial.com/ 479
}
if (!keyguardManager.isKeyguardSecure()) {
setError("User hasn't enabled Lock Screen");
return false;
}
if (!hasPermission()) {
setError("User hasn't granted permission to use Fingerprint");
return false;
}
if (!fingerprintManager.hasEnrolledFingerprints()) {
setError("User hasn't registered any fingerprints");
return false;
}
if (!initKeyStore()) {
return false;
}
return false;
}
@Nullable
@RequiresApi(api = Build.VERSION_CODES.M)
private Cipher createCipher(int mode) throws NoSuchPaddingException,
NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, InvalidKeyException,
InvalidAlgorithmParameterException {
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" +
KeyProperties.BLOCK_MODE_CBC + "/" +
KeyProperties.ENCRYPTION_PADDING_PKCS7);
@NonNull
@RequiresApi(api = Build.VERSION_CODES.M)
private KeyGenParameterSpec createKeyGenParameterSpec() {
return new KeyGenParameterSpec.Builder(MY_APP_ALIAS, KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build();
}
https://riptutorial.com/ 480
@RequiresApi(api = Build.VERSION_CODES.M)
private boolean initKeyStore() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore");
keyStore.load(null);
if (getLastIv() == null) {
KeyGenParameterSpec keyGeneratorSpec = createKeyGenParameterSpec();
keyGenerator.init(keyGeneratorSpec);
keyGenerator.generateKey();
}
} catch (Throwable t) {
setError("Failed init of keyStore & keyGenerator: " + t.getMessage());
return false;
}
return true;
}
@RequiresApi(api = Build.VERSION_CODES.M)
private void authenticate(CancellationSignal cancellationSignal,
FingerPrintAuthenticationListener authListener, int mode) {
try {
if (hasPermission()) {
Cipher cipher = createCipher(mode);
FingerprintManager.CryptoObject crypto = new
FingerprintManager.CryptoObject(cipher);
fingerprintManager.authenticate(crypto, cancellationSignal, 0, authListener,
null);
} else {
authListener.getCallback().onFailure("User hasn't granted permission to use
Fingerprint");
}
} catch (Throwable t) {
authListener.getCallback().onFailure("An error occurred: " + t.getMessage());
}
}
if (ivString != null) {
return decodeBytes(ivString);
}
https://riptutorial.com/ 481
}
return null;
}
@RequiresApi(api = Build.VERSION_CODES.M)
private boolean hasPermission() {
return ActivityCompat.checkSelfPermission(context,
Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED;
}
@RequiresApi(api = Build.VERSION_CODES.M)
public void savePassword(@NonNull String password, CancellationSignal cancellationSignal,
Callback callback) {
authenticate(cancellationSignal, new FingerPrintEncryptPasswordListener(callback,
password), Cipher.ENCRYPT_MODE);
}
@RequiresApi(api = Build.VERSION_CODES.M)
public void getPassword(CancellationSignal cancellationSignal, Callback callback) {
authenticate(cancellationSignal, new FingerPrintDecryptPasswordListener(callback),
Cipher.DECRYPT_MODE);
}
@RequiresApi(api = Build.VERSION_CODES.M)
public boolean encryptPassword(Cipher cipher, String password) {
try {
// Encrypt the text
if(password.isEmpty()) {
setError("Password is empty");
return false;
}
if (cipher == null) {
setError("Could not create cipher");
return false;
}
https://riptutorial.com/ 482
return true;
}
out[i/2] = (byte)(h*16+l);
}
return out;
}
@NonNull
private String decipher(Cipher cipher) throws IOException, IllegalBlockSizeException,
BadPaddingException {
String retVal = null;
String savedEncryptedPassword = getSavedEncryptedPassword();
if (savedEncryptedPassword != null) {
byte[] decodedPassword = decodeBytes(savedEncryptedPassword);
CipherInputStream cipherInputStream = new CipherInputStream(new
ByteArrayInputStream(decodedPassword), cipher);
https://riptutorial.com/ 483
for (int i = 0; i < values.size(); i++) {
bytes[i] = values.get(i).byteValue();
}
@RequiresApi(Build.VERSION_CODES.M)
protected class FingerPrintAuthenticationListener extends
FingerprintManager.AuthenticationCallback {
/**
* Called when a recoverable error has been encountered during authentication. The
help
* string is provided to give the user guidance for what went wrong, such as
* "Sensor dirty, please clean it."
* @param helpCode An integer identifying the error message
* @param helpString A human-readable string that can be shown in UI
*/
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
callback.onHelp(helpCode, helpString.toString());
}
/**
* Called when a fingerprint is recognized.
* @param result An object containing authentication-related data
*/
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result)
{
}
/**
* Called when a fingerprint is valid but not recognized.
*/
public void onAuthenticationFailed() {
callback.onFailure("Authentication failed");
}
public @NonNull
Callback getCallback() {
return callback;
}
https://riptutorial.com/ 484
@RequiresApi(api = Build.VERSION_CODES.M)
private class FingerPrintEncryptPasswordListener extends FingerPrintAuthenticationListener
{
} catch (Exception e) {
callback.onFailure("Encryption failed " + e.getMessage());
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
protected class FingerPrintDecryptPasswordListener extends
FingerPrintAuthenticationListener {
} catch (Exception e) {
callback.onFailure("Deciphering failed " + e.getMessage());
}
}
}
}
This activity below is a very basic example of how to get a user saved password and interact with
the helper.
https://riptutorial.com/ 485
private FingerPrintAuthHelper fingerPrintAuthHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
passwordTextView = (TextView) findViewById(R.id.password);
errorTextView = (TextView) findViewById(R.id.error);
// Start the finger print helper. In case this fails show error to user
private void startFingerPrintAuthHelper() {
fingerPrintAuthHelper = new FingerPrintAuthHelper(this);
if (!fingerPrintAuthHelper.init()) {
errorTextView.setText(fingerPrintAuthHelper.getLastError());
}
}
@NonNull
private FingerPrintAuthHelper.Callback getAuthListener(final boolean isGetPass) {
return new FingerPrintAuthHelper.Callback() {
@Override
public void onSuccess(String result) {
if (isGetPass) {
errorTextView.setText("Success!!! Pass = " + result);
} else {
errorTextView.setText("Encrypted pass = " + result);
}
}
@Override
public void onFailure(String message) {
errorTextView.setText("Failed - " + message);
}
@Override
public void onHelp(int helpCode, String helpString) {
errorTextView.setText("Help needed - " + helpString);
}
};
https://riptutorial.com/ 486
}
}
https://riptutorial.com/ 487
Chapter 100: Firebase
Introduction
Firebase is a mobile and web application platform with tools and infrastructure designed to help
developers build high-quality apps.
Features
Firebase Cloud Messaging, Firebase Auth, Realtime Database, Firebase Storage, Firebase
Hosting, Firebase Test Lab for Android, Firebase Crash Reporting.
Remarks
Examples
Create a Firebase user
@BindView(R.id.tIETSignUpEmail)
EditText mEditEmail;
@BindView(R.id.tIETSignUpPassword)
EditText mEditPassword;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@OnClick(R.id.btnSignUpSignUp)
void signUp() {
FormValidationUtils.clearErrors(mEditEmail, mEditPassword);
if (FormValidationUtils.isBlank(mEditEmail)) {
https://riptutorial.com/ 488
mEditEmail.setError("Please enter email");
return;
}
if (!FormValidationUtils.isEmailValid(mEditEmail)) {
mEditEmail.setError("Please enter valid email");
return;
}
if (TextUtils.isEmpty(mEditPassword.getText())) {
mEditPassword.setError("Please enter password");
return;
}
createUserWithEmailAndPassword(mEditEmail.getText().toString(),
mEditPassword.getText().toString());
}
@Override
protected int getLayoutResourceId() {
return R.layout.activity_sign_up;
}
}
@BindView(R.id.tIETLoginEmail)
EditText mEditEmail;
@BindView(R.id.tIETLoginPassword)
EditText mEditPassword;
@Override
protected void onResume() {
https://riptutorial.com/ 489
super.onResume();
FirebaseUser firebaseUser = mFirebaseAuth.getCurrentUser();
if (firebaseUser != null)
startActivity(new Intent(this, HomeActivity.class));
}
@Override
protected int getLayoutResourceId() {
return R.layout.activity_login;
}
@OnClick(R.id.btnLoginLogin)
void onSignInClick() {
FormValidationUtils.clearErrors(mEditEmail, mEditPassword);
if (FormValidationUtils.isBlank(mEditEmail)) {
FormValidationUtils.setError(null, mEditEmail, "Please enter email");
return;
}
if (!FormValidationUtils.isEmailValid(mEditEmail)) {
FormValidationUtils.setError(null, mEditEmail, "Please enter valid email");
return;
}
if (TextUtils.isEmpty(mEditPassword.getText())) {
FormValidationUtils.setError(null, mEditPassword, "Please enter password");
return;
}
signInWithEmailAndPassword(mEditEmail.getText().toString(),
mEditPassword.getText().toString());
}
DialogUtils.dismissProgressDialog();
if (task.isSuccessful()) {
Toast.makeText(LoginActivity.this, "Login Successful",
Toast.LENGTH_SHORT).show();
startActivity(new Intent(LoginActivity.this, HomeActivity.class));
finish();
} else {
Toast.makeText(LoginActivity.this,
task.getException().getMessage(),
Toast.LENGTH_SHORT).show();
}
}
});
}
@OnClick(R.id.btnLoginSignUp)
void onSignUpClick() {
https://riptutorial.com/ 490
startActivity(new Intent(this, SignUpActivity.class));
}
@OnClick(R.id.btnLoginForgotPassword)
void forgotPassword() {
startActivity(new Intent(this, ForgotPasswordActivity.class));
}
}
@BindView(R.id.tIETForgotPasswordEmail)
EditText mEditEmail;
private FirebaseAuth mFirebaseAuth;
private FirebaseAuth.AuthStateListener mAuthStateListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_forgot_password);
ButterKnife.bind(this);
mFirebaseAuth = FirebaseAuth.getInstance();
}
}
};
}
@Override
protected void onStart() {
super.onStart();
mFirebaseAuth.addAuthStateListener(mAuthStateListener);
}
@Override
protected void onStop() {
super.onStop();
if (mAuthStateListener != null) {
mFirebaseAuth.removeAuthStateListener(mAuthStateListener);
}
}
@OnClick(R.id.btnForgotPasswordSubmit)
void onSubmitClick() {
if (FormValidationUtils.isBlank(mEditEmail)) {
FormValidationUtils.setError(null, mEditEmail, "Please enter email");
https://riptutorial.com/ 491
return;
}
if (!FormValidationUtils.isEmailValid(mEditEmail)) {
FormValidationUtils.setError(null, mEditEmail, "Please enter valid email");
return;
}
@BindView(R.id.et_change_email)
EditText mEditText;
private FirebaseUser mFirebaseUser;
@OnClick(R.id.btn_change_email)
void onChangeEmailClick() {
FormValidationUtils.clearErrors(mEditText);
if (FormValidationUtils.isBlank(mEditText)) {
FormValidationUtils.setError(null, mEditText, "Please enter email");
return;
}
if (!FormValidationUtils.isEmailValid(mEditText)) {
FormValidationUtils.setError(null, mEditText, "Please enter valid email");
return;
}
changeEmail(mEditText.getText().toString());
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mFirebaseUser = mFirebaseAuth.getCurrentUser();
https://riptutorial.com/ 492
}
if (task.getException() instanceof
FirebaseAuthRecentLoginRequiredException) {
FragmentManager fm = getSupportFragmentManager();
ReAuthenticateDialogFragment reAuthenticateDialogFragment = new
ReAuthenticateDialogFragment();
reAuthenticateDialogFragment.show(fm,
reAuthenticateDialogFragment.getClass().getSimpleName());
}
}
});
}
@Override
protected int getLayoutResourceId() {
return R.layout.activity_change_email;
}
@Override
public void onReauthenticateSuccess() {
changeEmail(mEditText.getText().toString());
}
}
Change Password
@OnClick(R.id.btn_change_password)
void onChangePasswordClick() {
FormValidationUtils.clearErrors(mEditText);
if (FormValidationUtils.isBlank(mEditText)) {
FormValidationUtils.setError(null, mEditText, "Please enter password");
return;
}
changePassword(mEditText.getText().toString());
}
https://riptutorial.com/ 493
DialogUtils.showProgressDialog(this, "Changing Password", "Please wait...", false);
mFirebaseUser.updatePassword(password)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
DialogUtils.dismissProgressDialog();
if (task.isSuccessful()) {
showToast("Password updated successfully.");
return;
}
if (task.getException() instanceof
FirebaseAuthRecentLoginRequiredException) {
FragmentManager fm = getSupportFragmentManager();
ReAuthenticateDialogFragment reAuthenticateDialogFragment = new
ReAuthenticateDialogFragment();
reAuthenticateDialogFragment.show(fm,
reAuthenticateDialogFragment.getClass().getSimpleName());
}
}
});
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mFirebaseUser = mFirebaseAuth.getCurrentUser();
}
@Override
protected int getLayoutResourceId() {
return R.layout.activity_change_password;
}
@Override
public void onReauthenticateSuccess() {
changePassword(mEditText.getText().toString());
}
}
@BindView(R.id.et_dialog_reauthenticate_email)
EditText mEditTextEmail;
@BindView(R.id.et_dialog_reauthenticate_password)
EditText mEditTextPassword;
private OnReauthenticateSuccessListener mOnReauthenticateSuccessListener;
@OnClick(R.id.btn_dialog_reauthenticate)
void onReauthenticateClick() {
FormValidationUtils.clearErrors(mEditTextEmail, mEditTextPassword);
if (FormValidationUtils.isBlank(mEditTextEmail)) {
FormValidationUtils.setError(null, mEditTextEmail, "Please enter email");
return;
https://riptutorial.com/ 494
}
if (!FormValidationUtils.isEmailValid(mEditTextEmail)) {
FormValidationUtils.setError(null, mEditTextEmail, "Please enter valid email");
return;
}
if (TextUtils.isEmpty(mEditTextPassword.getText())) {
FormValidationUtils.setError(null, mEditTextPassword, "Please enter password");
return;
}
reauthenticateUser(mEditTextEmail.getText().toString(),
mEditTextPassword.getText().toString());
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mOnReauthenticateSuccessListener = (OnReauthenticateSuccessListener) context;
}
@OnClick(R.id.btn_dialog_reauthenticate_cancel)
void onCancelClick() {
dismiss();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_reauthenticate, container);
ButterKnife.bind(this, view);
return view;
}
@Override
public void onResume() {
super.onResume();
Window window = getDialog().getWindow();
window.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
https://riptutorial.com/ 495
WindowManager.LayoutParams.WRAP_CONTENT);
}
interface OnReauthenticateSuccessListener {
void onReauthenticateSuccess();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_CODE_PICK_IMAGE) {
String filePath = FileUtil.getPath(this, data.getData());
mUri = Uri.fromFile(new File(filePath));
https://riptutorial.com/ 496
uploadFile(mUri);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_READ_WRITE_EXTERNAL_STORAGE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
pickImage();
}
}
}
/**
* Step 1: Create a Storage
*
* @param view
https://riptutorial.com/ 497
*/
public void onCreateReferenceClick(View view) {
mStorageReference =
mFirebaseStorage.getReferenceFromUrl("gs://**something**.appspot.com");
showToast("Reference Created Successfully.");
findViewById(R.id.button_step_2).setEnabled(true);
}
/**
* Step 2: Create a directory named "Images"
*
* @param view
*/
public void onCreateDirectoryClick(View view) {
mStorageReferenceImages = mStorageReference.child("images");
showToast("Directory 'images' created Successfully.");
findViewById(R.id.button_step_3).setEnabled(true);
}
/**
* Step 3: Upload an Image File and display it on ImageView
*
* @param view
*/
public void onUploadFileClick(View view) {
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(MainActivity.this, new
String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_READ_WRITE_EXTERNAL_STORAGE);
else {
pickImage();
}
}
/**
* Step 4: Download an Image File and display it on ImageView
*
* @param view
*/
public void onDownloadFileClick(View view) {
downloadFile(mUri);
}
/**
* Step 5: Delete am Image File and remove Image from ImageView
*
* @param view
*/
public void onDeleteFileClick(View view) {
deleteFile(mUri);
}
if (okListener == null) {
okListener = new DialogInterface.OnClickListener() {
https://riptutorial.com/ 498
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
};
}
if (!TextUtils.isEmpty(title)) {
builder.setTitle(title);
}
builder.show();
}
StorageReference uploadStorageReference =
mStorageReferenceImages.child(uri.getLastPathSegment());
final UploadTask uploadTask = uploadStorageReference.putFile(uri);
showHorizontalProgressDialog("Uploading", "Please wait...");
uploadTask
.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
hideProgressDialog();
Uri downloadUrl = taskSnapshot.getDownloadUrl();
Log.d("MainActivity", downloadUrl.toString());
showAlertDialog(MainActivity.this, "Upload Complete",
downloadUrl.toString(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
findViewById(R.id.button_step_3).setEnabled(false);
findViewById(R.id.button_step_4).setEnabled(true);
}
});
Glide.with(MainActivity.this)
.load(downloadUrl)
.into(mImageView);
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
exception.printStackTrace();
// Handle unsuccessful uploads
hideProgressDialog();
}
})
.addOnProgressListener(MainActivity.this, new
OnProgressListener<UploadTask.TaskSnapshot>() {
@Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
int progress = (int) (100 * (float) taskSnapshot.getBytesTransferred()
/ taskSnapshot.getTotalByteCount());
Log.i("Progress", progress + "");
updateProgress(progress);
https://riptutorial.com/ 499
}
});
}
Glide.with(MainActivity.this)
.load(localFile)
.into(mImageView);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
// Handle any errors
hideProgressDialog();
exception.printStackTrace();
}
}).addOnProgressListener(new OnProgressListener<FileDownloadTask.TaskSnapshot>() {
@Override
public void onProgress(FileDownloadTask.TaskSnapshot taskSnapshot) {
int progress = (int) (100 * (float) taskSnapshot.getBytesTransferred() /
taskSnapshot.getTotalByteCount());
Log.i("Progress", progress + "");
updateProgress(progress);
}
});
}
https://riptutorial.com/ 500
showProgressDialog("Deleting", "Please wait...");
StorageReference storageReferenceImage =
mStorageReferenceImages.child(uri.getLastPathSegment());
storageReferenceImage.delete().addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
hideProgressDialog();
showAlertDialog(MainActivity.this, "Success", "File deleted successfully.",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mImageView.setImageResource(R.drawable.placeholder_image);
findViewById(R.id.button_step_3).setEnabled(true);
findViewById(R.id.button_step_4).setEnabled(false);
findViewById(R.id.button_step_5).setEnabled(false);
}
});
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "Firebase Storage");
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d("MainActivity", "failed to create Firebase Storage directory");
}
}
deleteFiles(mediaStorageDir);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
hideProgressDialog();
exception.printStackTrace();
}
});
}
By default, Firebase Storage rules applies Authentication restriction. If user is authenticated, only
then, he can perform operations on Firebase Storage, else he cannot. I have disabled the
authentication part in this demo by updating Storage rules. Previously, rules were looking like:
service firebase.storage {
match /b/**something**.appspot.com/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
service firebase.storage {
match /b/**something**.appspot.com/o {
https://riptutorial.com/ 501
match /{allPaths=**} {
allow read, write;
}
}
}
First of all you need to setup your project adding Firebase to your Android project following the
steps described in this topic.
dependencies {
compile 'com.google.firebase:firebase-messaging:11.0.4'
}
For example:
<service
android:name=".MyInstanceIdListenerService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<service
android:name=".MyFcmListenerService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
https://riptutorial.com/ 502
Here are simple implementations of the 2 services.
To retrieve the current registration token extend the FirebaseInstanceIdService class and override
the onTokenRefresh() method:
// Called if InstanceID token is updated. Occurs if the security of the previous token had
been
// compromised. This call is initiated by the InstanceID provider.
@Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
To receive messages, use a service that extends FirebaseMessagingService and override the
onMessageReceived method.
/**
* Called when message is received.
*
* @param remoteMessage Object representing the message received from Firebase Cloud
Messaging.
*/
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
String from = remoteMessage.getFrom();
// do whatever you want with this, post your own notification, or update local state
}
in Firebase can grouped user by their behavior like "AppVersion,free user,purchase user,or any
specific rules" and then send notification to specific group by send Topic Feature in fireBase.
to register user in topic use
FirebaseMessaging.getInstance().subscribeToTopic("Free");
https://riptutorial.com/ 503
More info in the dedicated topic Firebase Cloud Messaging.
Here are simplified steps (based on the official documentation) required to create a Firebase
project and connect it with an Android app.
2. Click Add Firebase to your Android app and follow the setup steps.
4. At the end, you'll download a google-services.json file. You can download this file again at
any time.
5. If you haven't done so already, copy the google-services.json file into your project's module
folder, typically app/.
The next step is to Add the SDK to integrate the Firebase libraries in the project.
1. Add rules to your root-level build.gradle file, to include the google-services plugin:
buildscript {
// ...
dependencies {
// ...
classpath 'com.google.gms:google-services:3.1.0'
}
}
Then, in your module Gradle file (usually the app/build.gradle), add the apply plugin line at the
bottom of the file to enable the Gradle plugin:
android {
// ...
https://riptutorial.com/ 504
}
dependencies {
// ...
compile 'com.google.firebase:firebase-core:11.0.4'
}
The final step is to add the dependencies for the Firebase SDK using one or more libraries
available for the different Firebase features.
com.google.firebase:firebase-core:11.0.4 Analytics
com.google.firebase:firebase-storage:11.0.4 Storage
com.google.firebase:firebase-auth:11.0.4 Authentication
com.google.firebase:firebase-ads:11.0.4 AdMob
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
Once it is done, create a child by editing your database address. For example:
https://your-project.firebaseio.com/ to https://your-project.firebaseio.com/chat
We will put data to this location from our Android device. You don't have to create the database
https://riptutorial.com/ 505
structure (tabs, fields... etc), it will be automatically created when you'll send Java object to
Firebase!
Create a Java object that contains all the attributes you want to send to the database:
if (FirebaseAuth.getInstance().getCurrentUser() == null) {
FirebaseAuth.getInstance().signInAnonymously().addOnCompleteListener(new
OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isComplete() && task.isSuccessful()){
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference reference = database.getReference("chat"); // reference
is 'chat' because we created the database at /chat
}
}
});
}
To send a value:
reference.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
ChatMessage msg = dataSnapshot.getValue(ChatMessage.class);
Log.d(TAG, msg.getUsername()+" "+msg.getMessage());
}
https://riptutorial.com/ 506
public void onChildRemoved(DataSnapshot dataSnapshot) {}
public void onChildMoved(DataSnapshot dataSnapshot, String s) {}
public void onCancelled(DatabaseError databaseError) {}
});
This example shows how to use the Firebase Cloud Messaging(FCM) platform. FCM is a
successor of Google Cloud Messaging(GCM). It does not require C2D_MESSAGE permissions
from the app users.
1. Create sample hello world project in Android Studio Your Android studio screen would look
like the following picture.
https://riptutorial.com/ 507
https://riptutorial.com/ 508
2. Next step is to set up firebase project. Visit https://console.firebase.google.com and create a
project with an identical name, so that you can track it easily.
and create a project with an identical name, so that you can track it easily.
https://riptutorial.com/ 509
3. Now it is time to add firebase to your sample android project you have just created. You will
need package name of your project and Debug signing certificate SHA-1(optional).
a. Package name - It can be found from the android manifest XML file.
b. Debug signing SHA-1 certificate - It can be found by running following command in the
terminal.
https://riptutorial.com/ 510
keypass android
Enter this information in the firebase console and add the app to firebase project. Once you click
on add app button, your browser would automatically download a JSON file named "google-
services.json".
4. Now copy the google-services.json file you have just downloaded into your Android app
module root directory.
https://riptutorial.com/ 511
https://riptutorial.com/ 512
5. Follow the instructions given on the firebase console as you proceed ahead. a. Add following
code line to your project level build.gradle
b. Add following code line at the end of your app level build.gradle.
c. Android studio would ask you to sync project. Click on Sync now.
6. Next task is to add two services. a. One extending FirebaseMessagingService with intent-
filter as following
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import com.google.firebase.messaging.FirebaseMessagingService;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import com.google.firebase.iid.FirebaseInstanceIdService;
https://riptutorial.com/ 513
9. Now it is time to capture the device registration token. Add following line of code to
MainActivity's onCreate method.
10. Once we have the access token, we can use firebase console to send out the notification.
Run the app on your android handset.
https://riptutorial.com/ 514
https://riptutorial.com/ 515
Chapter 101: Firebase App Indexing
Remarks
• When you opt to implement App Indexing then you may find lots of blogs, documentation out
there which may confuse you, in this case, I suggest you to stick to official docs provided by
Firebase-Google. Even if you want to use third party to do this, first try follow this
documentation because this will give you a clear idea how things are working.
• Google will take around 24 hours to index your content. So be patient. You can do testing to
make every thing is fine on your side.
• First example lets you support HTTP URL of your website to redirect in your App. This will
work such as, you have searched a query in the google search, results show one of your
website URL, whose app links are present in your app which is already installed. On clicking
this URL it will redirect you directly in your App Screen corresponding to that search result.
That's it I have discovered for this.
• Adding AppIndexing API indexes your content and used in Auto completions in Google
search Bar. Lets take example of inShorts Application for each page there is a headline and
small description. After reading 2 or 3 headlines, close the application and move to google
searchBar.
https://riptutorial.com/ 516
Try entering headline you just went through, you will get App page suggestion with that Headline
as Title. This is different from App suggestions you get while searching for Apps. This happens
because you have written AppIndexing API code for this particular page and title is same as you
have initialized in onCreate().
https://riptutorial.com/ 517
https://riptutorial.com/ 518
Specify the ACTION_VIEW intent action so that the intent filter can be reached from Google
Search.
< data> Add one or more tags, where each tag represents a URI format that resolves to the
activity. At minimum, the tag must include the android:scheme attribute. You can add additional
attributes to further refine the type of URI that the activity accepts. For example, you might have
multiple activities that accept similar URIs, but which differ simply based on the path name. In this
case, use the android:path attribute or its variants (pathPattern or pathPrefix) to differentiate which
activity the system should open for different URI paths.
< category> Include the BROWSABLE category. The BROWSABLE category is required in order
for the intent filter to be accessible from a web browser. Without it, clicking a link in a browser
cannot resolve to your app. The DEFAULT category is optional, but recommended. Without this
category, the activity can be started only with an explicit intent, using your app component name.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_schedule);
onNewIntent(getIntent());
}
Step 5 :- You can test this by using Android Debug Bridge command or studio configurations. Adb
command:- Launch your application and then run this command:-
Android Studio Configurations:- Android studio > Build > Edit Configuration >Launch
options>select URL>then type in your Url here >Apply and test.Run your application if “Run”
window shows error then you need to check your URL format with your applinks mentioned in
manifest otherwise it will successfully run,and redirect to page mentioned your URL if specified.
For Adding this to project you can find official doc easily but in this example I'm going to highlight
some of the key areas to be taken care of.
https://riptutorial.com/ 519
dependencies {
...
compile 'com.google.android.gms:play-services-appindexing:9.4.0'
...
}
import com.google.android.gms.appindexing.Action;
import com.google.android.gms.appindexing.AppIndex;
import com.google.android.gms.common.api.GoogleApiClient;
//If you know the values that to be indexed then you can initialize these variables in
onCreate()
@Override
protected void onCreate(Bundle savedInstanceState) {
mClient = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
mUrl = "http://examplepetstore.com/dogs/standard-poodle";
mTitle = "Standard Poodle";
mDescription = "The Standard Poodle stands at least 18 inches at the withers";
}
//If your data is coming from a network request, then initialize these value in onResponse()
and make checks for NPE so that your code won’t fall apart.
mClient.connect();
AppIndex.AppIndexApi.start(mClient, getAction());
@Override
protected void onStop() {
if (mTitle != null && mDescription != null && mUrl != null) //if your response fails then
check whether these are initialized or not
if (getAction() != null) {
AppIndex.AppIndexApi.end(mClient, getAction());
mClient.disconnect();
}
super.onStop();
}
https://riptutorial.com/ 520
.setObject(object)
.setActionStatus(Action.STATUS_TYPE_COMPLETED)
.build();
}
https://riptutorial.com/ 521
Chapter 102: Firebase Cloud Messaging
Introduction
Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably
deliver messages at no cost.
Using FCM, you can notify a client app that new email or other data is available to sync. You can
send notification messages to drive user reengagement and retention. For use cases such as
instant messaging, a message can transfer a payload of up to 4KB to a client app.
Examples
Set Up a Firebase Cloud Messaging Client App on Android
1. Complete the Installation and setup part to connect your app to Firebase.
This will create the project in Firebase.
2. Add the dependency for Firebase Cloud Messaging to your module-level build.gradle file:
dependencies {
compile 'com.google.firebase:firebase-messaging:10.2.1'
}
FCM clients require devices running Android 2.3 or higher that also have the Google Play Store
app installed, or an emulator running Android 2.3 with Google APIs.
<service
android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service
android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
Registration token
On initial startup of your app, the FCM SDK generates a registration token for the client app
https://riptutorial.com/ 522
instance.
If you want to target single devices or create device groups, you'll need to access this token by
extending FirebaseInstanceIdService.
The onTokenRefresh callback fires whenever a new token is generated and you can use the method
FirebaseInstanceID.getToken() to retrieve the current token.
Example:
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the InstanceID
token
* is initially generated so this is where you would retrieve the token.
*/
@Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
This code that i have implemnted in my app for pushing image,message and
also link for opening in your webView
This is my FirebaseMessagingService
/**
* Create and show a simple notification containing the received FCM message.
*/
https://riptutorial.com/ 523
intent.putExtra("LINK",link);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */,
intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setLargeIcon(image)/*Notification icon image*/
.setSmallIcon(R.drawable.hindi)
.setContentTitle(messageBody)
.setStyle(new NotificationCompat.BigPictureStyle()
.bigPicture(image))/*Notification with Image*/
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}}
And this is MainActivity to open link in my WebView or other browser depand on your requirement
through intents.
if (getIntent().getExtras() != null) {
if (getIntent().getStringExtra("LINK")!=null) {
Intent i=new Intent(this,BrowserActivity.class);
i.putExtra("link",getIntent().getStringExtra("LINK"));
i.putExtra("PUSH","yes");
NewsListActivity.this.startActivity(i);
finish();
}}
Receive Messages
To receive messages, use a service that extends FirebaseMessagingService and override the
onMessageReceived method.
/**
https://riptutorial.com/ 524
* Called when message is received.
*
* @param remoteMessage Object representing the message received from Firebase Cloud
Messaging.
*/
@Override
public void onMessageReceived(RemoteMessage message) {
String from = message.getFrom();
//.....
}
When the app is in the background, Android directs notification messages to the system tray. A
user tap on the notification opens the app launcher by default.
This includes messages that contain both notification and data payload (and all messages sent
from the Notifications console). In these cases, the notification is delivered to the device's system
tray, and the data payload is delivered in the extras of the intent of your launcher Activity.
Subscribe to a topic
Client apps can subscribe to any existing topic, or they can create a new topic. When a client app
subscribes to a new topic name, a new topic of that name is created in FCM and any client can
subsequently subscribe to it.
To subscribe to a topic use the subscribeToTopic() method specifying the topic name:
FirebaseMessaging.getInstance().subscribeToTopic("myTopic");
https://riptutorial.com/ 525
Chapter 103: Firebase Crash Reporting
Examples
How to add Firebase Crash Reporting to your app
In order to add Firebase Crash Reporting to your app, perform the following steps:
• Copy the google-services.json file from your project into your in app/ directory.
• Add the following rules to your root-level build.gradle file in order to include the google-
services plugin:
buildscript {
// ...
dependencies {
// ...
classpath 'com.google.gms:google-services:3.0.0'
}
}
• In your module Gradle file, add the apply plugin line at the bottom of the file to enable the
Gradle plugin:
• Add the dependency for Crash Reporting to your app-level build.gradle file:
compile 'com.google.firebase:firebase-crash:10.2.1'
• You can then fire a custom exception from your application by using the following line:
• If you want to add custom logs to a console, you can use the following code:
FirebaseCrash.log("Level 2 completed.");
• Official documentation
• Stack Overflow dedicated topic
https://riptutorial.com/ 526
How to report an error
Firebase Crash Reporting automatically generates reports for fatal errors (or uncaught
exceptions).
You can check in the log when FirebaseCrash initialized the module:
FirebaseCrash.log("Activity created");
https://riptutorial.com/ 527
Chapter 104: Firebase Realtime DataBase
Remarks
Examples
Firebase Realtime DataBase event handler
myRef.setValue("Hello, World!");
@Override
public void onCancelled(DatabaseError error) {
// Failed to read value
Log.w(TAG, "Failed to read value.", error.toException());
}
});
https://riptutorial.com/ 528
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "postComments:onCancelled", databaseError.toException());
Toast.makeText(mContext, "Failed to load comments.",
Toast.LENGTH_SHORT).show();
}
};
ref.addChildEventListener(childEventListener);
Quick setup
1. Complete the Installation and setup part to connect your app to Firebase.
This will create the project in Firebase.
2. Add the dependency for Firebase Realtime Database to your module-level build.gradle file:
compile 'com.google.firebase:firebase-database:10.2.1'
Now you are ready to work with the Realtime Database in Android.
For example you write a Hello World message to the database under the message key.
myRef.setValue("Hello, World!");
Designing and understanding how to retrieve realtime data from the Firebase
Database
This example assumes that you have already set up a Firebase Realtime Database. If you are a
starter, then please inform yourself here on how to add Firebase to your Android project.
https://riptutorial.com/ 529
First, add the dependency of the Firebase Database to the app level build.gradle file:
compile 'com.google.firebase:firebase-database:9.4.0'
Now, let us create a chat app which stores data into the Firebase Database.
[
{
"name":"John Doe",
"message":"My first Message"
},
{
"name":"John Doe",
"message":"Second Message"
},
{
"name":"John Doe",
"message":"Third Message"
}
]
chatDb.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
// This function is called for every child id chat in this case, so using the above
https://riptutorial.com/ 530
// example, this function is going to be called 3 times.
// Use the getValue function in the dataSnapshot and pass the object's class name to
// which you want to convert and get data. In this case it is Chat.class.
chat = dataSnapshot.getValue(Chat.class);
// Now you can use this chat object and add it into an ArrayList or something like
// that and show it in the recycler view.
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
// This function is called when any of the node value is changed, dataSnapshot will
// get the data with the key of the child, so you can swap the new value with the
// old one in the ArrayList or something like that.
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
// This function is called when any of the child node is removed. dataSnapshot will
// get the data with the key of the child.
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
// This function is called when any of the child nodes is moved to a different
position.
@Override
public void onCancelled(DatabaseError databaseError) {
// If anything goes wrong, this function is going to be called.
Now get a reference to the chats node as done in the retrieving session:
https://riptutorial.com/ 531
DatabaseReference chatDb = FirebaseDatabase.getInstance().getReference().child("chats");
Before you start adding data, keep in mind that you need one more deep reference since a chat
node has several more nodes and adding a new chat means adding a new node containing the
chat details. We can generate a new and unique name of the node using the push() function on
the DatabaseReference object, which will return another DatabaseReference, which in turn points to a
newly formed node to insert the chat data.
Example
// The parameter is the chat object that was newly created a few lines above.
chatDb.push().setValue(chat);
The setValue() function will make sure that all of the application's onDataChanged functions are
getting called (including the same device), which happens to be the attached listener of the "chats"
node.
Denormalization and a flat database structure is neccessary to efficiently download separate calls.
With the following structure, it is also possible to maintain two-way relationships. The
disadvantage of this approach is, that you always need to update the data in multiple places.
For an example, imagine an app which allows the user to store messages to himself (memos).
|--database
|-- memos
|-- memokey1
|-- title: "Title"
|-- content: "Message"
|-- memokey2
|-- title: "Important Title"
|-- content: "Important Message"
|-- users
|-- userKey1
|-- name: "John Doe"
|-- memos
|-- memokey1 : true //The values here don't matter, we only need the keys.
|-- memokey2 : true
|-- userKey2
|-- name: "Max Doe"
https://riptutorial.com/ 532
//toMap() is necessary for the push process
private Map<String, Object> toMap() {
HashMap<String, Object> result = new HashMap<>();
result.put("title", title);
result.put("content", content);
return result;
}
}
@Override
public void onCancelled(DatabaseError databaseError) { }
});
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) { }
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) { }
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) { }
@Override
https://riptutorial.com/ 533
public void onCancelled(DatabaseError databaseError) { }
}
Creating a memo
FirebaseDatabase.getInstance().getReference().updateChildren(childUpdates);
|--database
|-- memos
|-- memokey1
|-- title: "Title"
|-- content: "Message"
|-- memokey2
|-- title: "Important Title"
|-- content: "Important Message"
|-- generatedMemokey3
|-- title: "Important numbers"
|-- content: "1337, 42, 3.14159265359"
|-- users
|-- userKey1
|-- name: "John Doe"
|-- memos
|-- memokey1 : true //The values here don't matter, we only need the keys.
|-- memokey2 : true
|-- generatedMemokey3 : true
|-- userKey2
|-- name: "Max Doe"
Before we get our hands dirty with code, I feel it is necessary to understand how data is stored in
firebase. Unlike relational databases, firebase stores data in JSON format. Think of each row in a
relational database as a JSON object (which is basically unordered key-value pair). So the column
name becomes key and the value stored in that column for one particular row is the value. This
https://riptutorial.com/ 534
way the entire row is represented as a JSON object and a list of these represent an entire
database table. The immediate benefit that I see for this is schema modification becomes much
more cheaper operation compared to old RDBMS. It is easier to add a couple of more attributes to
a JSON than altering a table structure.
{
"user_base" : {
"342343" : {
"email" : "[email protected]",
"authToken" : "some string",
"name" : "Kaushal",
"phone" : "+919916xxxxxx",
"serviceProviderId" : "firebase",
"signInServiceType" : "google",
},
"354895" : {
"email" : "[email protected]",
"authToken" : "some string",
"name" : "devil",
"phone" : "+919685xxxxxx",
"serviceProviderId" : "firebase",
"signInServiceType" : "github"
},
"371298" : {
"email" : "[email protected]",
"authToken" : "I am batman",
"name" : "Bruce Wayne",
"phone" : "+14085xxxxxx",
"serviceProviderId" : "firebase",
"signInServiceType" : "shield"
}
},
"user_prefs": {
"key1":{
"data": "for key one"
},
"key2":{
"data": "for key two"
},
"key3":{
"data": "for key three"
}
},
//other structures
}
This clearly shows how data that we used to store in relational databases can be stored in JSON
format. Next let's see how to read this data in android devices.
I am gonna assume you already know about adding gradle dependencies firebase in android
studio. If you don't just follow the guide from here. Add your app in firebase console, gradle sync
android studio after adding dependencies. All dependencies are not needed just firebase database
https://riptutorial.com/ 535
and firebase auth.
Now that we know how data is stored and how to add gradle dependencies let's see how to use
the imported firebase android SDK to retrieve data.
from here you can chain multiple child() method calls to point to the data you are interested in. For
example if data is stored as depicted in previous section and you want to point to Bruce Wayne
user you can use:
Now that we have the reference of the data we want to fetch, we can use listeners to fetch data in
android apps. Unlike the traditional calls where you fire REST API calls using retrofit or volley,
here a simple callback listener is required to get the data. Firebase sdk calls the callback methods
and you are done.
There are basically two types of listeners you can attach, one is ValueEventListener and the other
one is ChildEventListener (described in next section). For any change in data under the node we
have references and added listeners to, value event listeners return the entire JSON structure and
child event listener returns specific child where the change has happened. Both of these are useful
in their own way. To fetch the data from firebase we can add one or more listeners to a firebase
database reference (list userDBRef we created earlier).
userDBRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
User bruceWayne = dataSnapshot.child("371298").getValue(User.class);
// Do something with the retrieved data or Bruce Wayne
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.e("UserListActivity", "Error occured");
// Do something about the error
});
https://riptutorial.com/ 536
Did you notice the Class type passed. DataSnapshot can convert JSON data into our defined
POJOs, simple pass the right class type.
If your use case does not require the entire data (in our case user_base table) every time some
little change occurs or say you want to fetch the data only once, you can use
addListenerForSingleValueEvent() method of Database reference. This fires the callback only
once.
userDBRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Do something
}
@Override
public void onCancelled(DatabaseError databaseError) {
// Do something about the error
});
Above samples will give you the value of the JSON node. To get the key simply call:
Take a use case, like a chat app or a collaborative grocery list app (that basically requires a list of
objects to be synced across users). If you use firebase database and add a value event listener to
the chat parent node or grocery list parent node, you will end with entire chat structure from the
beginning of time (i meant beginning of your chat) every time a chat node is added (i.e. anyone
says hi). That we don't want to do, what we are interested in is only the new node or only the old
node that got deleted or modified, the unchanged ones should not be returned.
In this case we can use ChildEvenListener. Without any further adieu, here is code sample (see
prev sections for sample JSON data):
userDBRef.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
//If not dealing with ordered data forget about this
}
https://riptutorial.com/ 537
@Override
public void onCancelled(DatabaseError databaseError) {
});
Method names are self explanatory. As you can see whenever a new user is added or some
property of existing user is modified or user is deleted or removed appropriate callback method of
child event listener is called with relevant data. So if you are keeping UI refreshed for say chat
app, get the JSON from onChildAdded() parse into POJO and fit it in your UI. Just remember to
remove your listener when user leaves the screen.
onChildChanged() gives the entire child value with changed properties (new ones).
When you have a huge JSON database, adding a value event listener doesn't make sense. It will
return the huge JSON and parsing it would be time consuming. In such cases we can use
pagination and fetch part of data and display or process it. Kind of like lazy loading or like fetching
old chats when user clicks on show older chat. In this case Query can used.
Let's take the our old example in previous sections. The user base contains 3 users, if it grows to
say 3 hundred thousand user and you want to fetch the user list in batches of 50:
// class level
final int limit = 50;
int start = 0;
// event level
Query userListQuery = userDBRef.orderByChild("email").limitToFirst(limit)
.startAt(start)
userListQuery.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Do something
start += (limit+1);
}
@Override
public void onCancelled(DatabaseError databaseError) {
// Do something about the error
});
Here value or child events can be added and listened to. Call query again to fetch next 50. Make
sure to add the orderByChild() method, this will not work without that. Firebase needs to know
the order by which you are paginating.
https://riptutorial.com/ 538
Chapter 105: FloatingActionButton
Introduction
Floating action button is used for a special type of promoted action,it animates onto the screen as
an expanding piece of material, by default. The icon within it may be animated,also FAB may
move differently than other UI elements because of their relative importance. A floating action
button represents the primary action in an application which can simply trigger an action or
navigate somewhere.
Parameters
Parameter Detail
Remarks
Floating action buttons are used for a special type of promoted action. They are distinguished by a
circled icon floating above the UI and have special motion behaviors related to morphing,
launching, and the transferring anchor point.
Only one floating action button is recommended per screen to represent the most common action.
Before using the FloatingActionButton you must add the design support library dependency in the
build.gradle file:
dependencies {
compile 'com.android.support:design:25.1.0'
}
Official Documentation:
https://developer.android.com/reference/android/support/design/widget/FloatingActionButton.html
https://riptutorial.com/ 539
Material Design Specifications:
https://material.google.com/components/buttons-floating-action-button.html
Examples
How to add the FAB to the layout
To use a FloatingActionButton just add the dependency in the build.gradle file as described in the
remarks section.
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/my_icon" />
An example:
Color
The background color of this view defaults to the your theme's colorAccent.
In the above image if the src only points to + icon (by default 24x24 dp),to get the background
color of full circle you can use app:backgroundTint="@color/your_colour"
https://riptutorial.com/ 540
mFab.setRippleColor(your color in int);
Positioning
It is recommended to place 16dp minimum from the edge on mobile,and 24dp minimum on
tablet/desktop.
Note : Once you set an src excepting to cover the full area of FloatingActionButton make sure you
have the right size of that image to get the best result.
If you only want to change only the Interior icon use a 24 x 24dp icon for default size
To show and hide a FloatingActionButton with the default animation, just call the methods show()
and hide(). It's good practice to keep a FloatingActionButton in the Activity layout instead of putting
it in a Fragment, this allows the default animations to work when showing and hiding.
• Three Tabs
• Show FloatingActionButton for the first and third Tab
• Hide the FloatingActionButton on the middle Tab
FloatingActionButton fab;
ViewPager viewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
if (position == 0) {
fab.setImageResource(android.R.drawable.ic_dialog_email);
fab.show();
https://riptutorial.com/ 541
} else if (position == 2) {
fab.setImageResource(android.R.drawable.ic_dialog_map);
fab.show();
} else {
fab.hide();
}
}
@Override
public void onPageScrolled(int position, float positionOffset, int
positionOffsetPixels) {}
@Override
public void onPageScrollStateChanged(int state) {}
});
}
}
Result:
https://riptutorial.com/ 542
Show and Hide FloatingActionButton on Scroll
Starting with the Support Library version 22.2.1, it's possible to show and hide a
FloatingActionButton from scrolling behavior using a FloatingActionButton.Behavior sublclass that
takes advantage of the show() and hide() methods.
Note that this only works with a CoordinatorLayout in conjunction with inner Views that support
Nested Scrolling, such as RecyclerView and NestedScrollView.
This ScrollAwareFABBehavior class comes from the Android Guides on Codepath (cc-wiki with attribution
required)
@Override
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final
FloatingActionButton child,
final View directTargetChild, final View target, final
int nestedScrollAxes) {
// Ensure we react to vertical scrolling
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
|| super.onStartNestedScroll(coordinatorLayout, child, directTargetChild,
target, nestedScrollAxes);
}
https://riptutorial.com/ 543
@Override
public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final
FloatingActionButton child,
final View target, final int dxConsumed, final int dyConsumed,
final int dxUnconsumed, final int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed);
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
// User scrolled down and the FAB is currently visible -> hide the FAB
child.hide();
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
// User scrolled up and the FAB is currently not visible -> show the FAB
child.show();
}
}
}
In the FloatingActionButton layout xml, specify the app:layout_behavior with the fully-qualified-
class-name of ScrollAwareFABBehavior:
app:layout_behavior="com.example.app.ScrollAwareFABBehavior"
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="6dp">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:elevation="0dp"
app:layout_scrollFlags="scroll|enterAlways"
/>
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
app:tabMode="fixed"
android:layout_below="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
https://riptutorial.com/ 544
android:background="?attr/colorPrimary"
app:elevation="0dp"
app:tabTextColor="#d3d3d3"
android:minHeight="?attr/actionBarSize"
/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_below="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
app:layout_behavior="com.example.app.ScrollAwareFABBehavior"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
https://riptutorial.com/ 545
Setting behaviour of FloatingActionButton
For example:
<android.support.design.widget.FloatingActionButton
app:layout_behavior=".MyBehavior" />
https://riptutorial.com/ 546
Chapter 106: Formatting phone numbers with
pattern.
Introduction
This example show you how to format phone numbers with a patter
compile 'com.googlecode.libphonenumber:libphonenumber:7.2.2'
Examples
Patterns + 1 (786) 1234 5678
Given a normalized phone number like +178612345678 we will get a formatted number with the
provided pattern.
numberFormat.pattern = "(\\d{3})(\\d{3})(\\d{4})";
newNumberFormats.add(numberFormat);
try {
phoneNumberPN = phoneNumberUtil.parse(phoneNumber, Locale.US.getCountry());
phoneNumber = phoneNumberUtil.formatByPattern(phoneNumberPN,
PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL, newNumberFormats);
} catch (NumberParseException e) {
e.printStackTrace();
}
return phoneNumber;
}
https://riptutorial.com/ 547
Chapter 107: Formatting Strings
Examples
Format a string resource
You can add wildcards in string resources and populate them at runtime:
1. Edit strings.xml
Data types like int, float, double, long, boolean can be formatted to string using String.valueOf().
https://riptutorial.com/ 548
Chapter 108: Fragments
Introduction
Introduction about Fragments and their intercommunication mechanism
Syntax
• void onActivityCreated(Bundle savedInstanceState) // Called when the fragment's activity
has been created and this fragment's view hierarchy instantiated.
• void onActivityResult(int requestCode, int resultCode, Intent data) // Receive the result from
a previous call to startActivityForResult(Intent, int).
• void onAttach(Activity activity) // This method was deprecated in API level 23. Use
onAttach(Context) instead.
• void onAttach(Context context) // Called when a fragment is first attached to its context.
• void onDetach() // Called when the fragment is no longer attached to its activity.
• void onResume() // Called when the fragment is visible to the user and actively running.
https://riptutorial.com/ 549
• void onSaveInstanceState(Bundle outState) // Called to ask the fragment to save its current
dynamic state, so it can later be reconstructed in a new instance of its process is restarted.
Remarks
A Fragment represents a behavior or a portion of user interface in an Activity. You can combine
multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple
activities. You can think of a fragment as a modular section of an activity, which has its own
lifecycle, receives its own input events, and which you can add or remove while the activity is
running (sort of like a "sub activity" that you can reuse in different activities).
Constructor
Every fragment must have an empty constructor, so it can be instantiated when restoring its
activity's state. It is strongly recommended that subclasses do not have other constructors with
parameters, since these constructors will not be called when the fragment is re-instantiated;
instead, arguments can be supplied by the caller with setArguments(Bundle) and later retrieved by
the Fragment with getArguments().
Examples
The newInstance() pattern
Although it is possible to create a fragment constructor with parameters, Android internally calls
the zero-argument constructor when recreating fragments (for example, if they are being restored
after being killed for Android's own reasons). For this reason, it is not advisable to rely on a
constructor that has parameters.
To ensure that your expected fragment arguments are always present you can use a static
newInstance() method to create the fragment, and put whatever parameters you want in to a
bundle that will be available when creating a new instance.
import android.os.Bundle;
import android.support.v4.app.Fragment;
https://riptutorial.com/ 550
private String mName;
// Required
public MyFragment(){}
// The static constructor. This is the only way that you should instantiate
// the fragment yourself
public static MyFragment newInstance(final String name) {
final MyFragment myFragment = new MyFragment();
// The 1 below is an optimization, being the number of arguments that will
// be added to this bundle. If you know the number of arguments you will add
// to the bundle it stops additional allocations of the backing map. If
// unsure, you can construct Bundle without any arguments
final Bundle args = new Bundle(1);
// This stores the argument as an argument in the bundle. Note that even if
// the 'name' parameter is NULL then this will work, so you should consider
// at this point if the parameter is mandatory and if so check for NULL and
// throw an appropriate error if so
args.putString(NAME_ARG, name);
myFragment.setArguments(args);
return myFragment;
}
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Bundle arguments = getArguments();
if (arguments == null || !arguments.containsKey(NAME_ARG)) {
// Set a default or error as you see fit
} else {
mName = arguments.getString(NAME_ARG);
}
}
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
MyFragment mFragment = MyFragment.newInstance("my name");
ft.replace(R.id.placeholder, mFragment);
//R.id.placeholder is where we want to load our fragment
ft.commit();
This pattern is a best practice to ensure that all the needed arguments will be passed to fragments
on creation. Note that when the system destroys the fragment and re-creates it later, it will
automatically restore its state - but you must provide it with an onSaveInstanceState(Bundle)
implementation.
First of all, we need to add our first Fragment at the beginning, we should do it in the onCreate()
method of our Activity:
if (null == savedInstanceState) {
https://riptutorial.com/ 551
getSupportFragmentManager().beginTransaction()
.addToBackStack("fragmentA")
.replace(R.id.container, FragmentA.newInstance(), "fragmentA")
.commit();
}
Next, we need to manage our backstack. The easiest way is using a function added in our activity
that is used for all FragmentTransactions.
//If fragment is already on stack, we can pop back stack to prevent stack infinite growth
if (getSupportFragmentManager().findFragmentByTag(tag) != null) {
getSupportFragmentManager().popBackStack(tag,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
Finally, we should override onBackPressed() to exit the application when going back from the last
Fragment available in the backstack.
@Override
public void onBackPressed() {
int fragmentsInStack = getSupportFragmentManager().getBackStackEntryCount();
if (fragmentsInStack > 1) { // If we have more than one fragment, pop back stack
getSupportFragmentManager().popBackStack();
} else if (fragmentsInStack == 1) { // Finish activity, if only one fragment left, to
prevent leaving empty screen
finish();
} else {
super.onBackPressed();
}
}
Execution in activity:
replaceFragment(FragmentB.newInstance(), "fragmentB");
https://riptutorial.com/ 552
Pass data from Activity to Fragment using Bundle
All fragments should have an empty constructor (i.e. a constructor method having no input
arguments). Therefore, in order to pass your data to the Fragment being created, you should use
the setArguments() method. This methods gets a bundle, which you store your data in, and stores
the Bundle in the arguments. Subsequently, this Bundle can then be retrieved in onCreate() and
onCreateView() call backs of the Fragment.
Activity:
Fragment:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState) {
String myValue = this.getArguments().getString("message");
...
}
If you need to send events from fragment to activity, one of the possible solutions is to define
callback interface and require that the host activity implement it.
Example
Send callback to an activity, when fragment's button clicked
First of all, define callback interface:
@Override
https://riptutorial.com/ 553
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof SampleCallback) {
callback = (SampleCallback) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement SampleCallback");
}
}
@Override
public void onDetach() {
super.onDetach();
callback = null;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState) {
final View view = inflater.inflate(R.layout.sample, container, false);
// Add button's click listener
view.findViewById(R.id.actionButton).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
callback.onButtonClicked(); // Invoke callback here
}
});
return view;
}
}
// ... Skipped code with settings content view and presenting the fragment
@Override
public void onButtonClicked() {
// Invoked when fragment's button has been clicked
}
}
To animate the transition between fragments, or to animate the process of showing or hiding a
fragment you use the FragmentManager to create a FragmentTransaction.
For a single FragmentTransaction, there are two different ways to perform animations: you can use
a standard animation or you can supply your own custom animations.
FragmentTransaction.TRANSIT_NONE
FragmentTransaction.TRANSIT_FRAGMENT_OPEN
https://riptutorial.com/ 554
FragmentTransaction.TRANSIT_FRAGMENT_CLOSE
FragmentTransaction.TRANSIT_FRAGMENT_FADE
getSupportFragmentManager()
.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.replace(R.id.contents, new MyFragment(), "MyFragmentTag")
.commit();
The enter and exit animations will be played for FragmentTransactions that do not involve popping
fragments off of the back stack. The popEnter and popExit animations will be played when popping
a fragment off of the back stack.
The following code shows how you would replace a fragment by sliding out one fragment and
sliding the other one in it's place.
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right)
.replace(R.id.contents, new MyFragment(), "MyFragmentTag")
.commit();
The XML animation definitions would use the objectAnimator tag. An example of slide_in_left.xml
might look something like this:
Additional Resources
https://riptutorial.com/ 555
In this sample, we have a MainActivity that hosts two fragments, SenderFragment and
ReceiverFragment, for sending and receiving a message (a simple String in this case) respectively.
A Button in SenderFragment initiates the process of sending the message. A TextView in the
ReceiverFragment is updated when the message is received by it.
Following is the snippet for the MainActivity with comments explaining the important lines of code:
// Our MainActivity implements the interface defined by the SenderFragment. This enables
// communication from the fragment to the activity
public class MainActivity extends AppCompatActivity implements
SenderFragment.SendMessageListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* This method is called when we click on the button in the SenderFragment
* @param message The message sent by the SenderFragment
*/
@Override
public void onSendMessage(String message) {
// Find our ReceiverFragment using the SupportFragmentManager and the fragment's id
ReceiverFragment receiverFragment = (ReceiverFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_receiver);
The layout file for the MainActivity hosts two fragments inside a LinearLayout :
<fragment
android:id="@+id/fragment_sender"
android:name="com.naru.fragmentcommunication.SenderFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
https://riptutorial.com/ 556
tools:layout="@layout/fragment_sender" />
<fragment
android:id="@+id/fragment_receiver"
android:name="com.naru.fragmentcommunication.ReceiverFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
tools:layout="@layout/fragment_receiver" />
</LinearLayout>
The SenderFragment exposes an interface SendMessageListener that helps the MainActivity know
when Button in the SenderFragment was clicked.
Following is the code snippet for the SenderFragment explaining the important lines of code:
/**
* This interface is created to communicate between the activity and the fragment. Any
activity
* which implements this interface will be able to receive the message that is sent by this
* fragment.
*/
public interface SendMessageListener {
void onSendMessage(String message);
}
/**
* API LEVEL >= 23
* <p>
* This method is called when the fragment is attached to the activity. This method here will
* help us to initialize our reference variable, 'commander' , for our interface
* 'SendMessageListener'
*
* @param context
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
// Try to cast the context to our interface SendMessageListener i.e. check whether the
// activity implements the SendMessageListener. If not a ClassCastException is thrown.
try {
commander = (SendMessageListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ "must implement the SendMessageListener interface");
}
}
/**
* API LEVEL < 23
* <p>
* This method is called when the fragment is attached to the activity. This method here will
* help us to initialize our reference variable, 'commander' , for our interface
* 'SendMessageListener'
*
https://riptutorial.com/ 557
* @param activity
*/
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Try to cast the context to our interface SendMessageListener i.e. check whether the
// activity implements the SendMessageListener. If not a ClassCastException is thrown.
try {
commander = (SendMessageListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ "must implement the SendMessageListener interface");
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
// Inflate view for the sender fragment.
View view = inflater.inflate(R.layout.fragment_receiver, container, false);
// Call our interface method. This enables us to call the implemented method
// in the activity, from where we can send the message to the
ReceiverFragment.
commander.onSendMessage("HELLO FROM SENDER FRAGMENT!");
}
}
});
return view;
}
}
<Button
android:id="@+id/bSend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SEND"
android:layout_gravity="center_horizontal" />
</LinearLayout>
https://riptutorial.com/ 558
The ReceiverFragment is simple and exposes a simple public method to updates its TextView.
When the MainActivity receives the message from the SenderFragment it calls this public method of
the ReceiverFragment
Following is the code snippet for the ReceiverFragment with comments explaining the important
lines of code :
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
// Inflate view for the sender fragment.
View view = inflater.inflate(R.layout.fragment_receiver, container, false);
return view;
}
/**
* Method that is called by the MainActivity when it receives a message from the
SenderFragment.
* This method helps update the text in the TextView to the message sent by the
SenderFragment.
* @param message Message sent by the SenderFragment via the MainActivity.
*/
public void showMessage(String message) {
tvMessage.setText(message);
}
}
https://riptutorial.com/ 559
Chapter 109: Fresco
Introduction
Fresco is a powerful system for displaying images in Android applications.
In Android 4.x and lower, Fresco puts images in a special region of Android memory (called
ashmem). This lets your application run faster - and suffer the dreaded OutOfMemoryError much
less often.
Remarks
How to set up dependencies in the app level build.gradle file:
dependencies {
// Your app's other dependencies.
compile 'com.facebook.fresco:fresco:0.14.1' // Or a newer version if available.
}
Examples
Getting Started with Fresco
If you need additional features, like animated GIF or WebP support, you have to add the
corresponding Fresco artifacts as well.
Fresco needs to be initialized. You should only do this 1 time, so placing the initialization in your
Application is a good idea. An example for this would be:
If you want to load remote images from a server, your app needs the internt permission. Simply
add it to your AndroidManifest.xml:
https://riptutorial.com/ 560
Then, add a SimpleDraweeView to your XML layout. Fresco does not support wrap_content for image
dimensions since you might have multiple images with different dimensions (placeholder image,
error image, actual image, ...).
So you can either add a SimpleDraweeView with fixed dimensions (or match_parent):
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/my_image_view"
android:layout_width="120dp"
android:layout_height="120dp"
fresco:placeholderImage="@drawable/placeholder" />
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/my_image_view"
android:layout_width="120dp"
android:layout_height="wrap_content"
fresco:viewAspectRatio="1.33"
fresco:placeholderImage="@drawable/placeholder" />
That's it! You should see your placeholder drawable until the network image has been fetched.
First, in addition to the normal Fresco Gradle dependency, you have to add the OkHttp 3
dependency to your build.gradle:
When you initialize Fresco (usually in your custom Application implementation), you can now
specify your OkHttp client:
This example assumes that you have already added Fresco to your app (see this example):
https://riptutorial.com/ 561
SimpleDraweeView img = new SimpleDraweeView(context);
ImageRequest request = ImageRequestBuilder
.newBuilderWithSource(Uri.parse("http://example.com/image.png"))
.setProgressiveRenderingEnabled(true) // This is where the magic happens.
.build();
https://riptutorial.com/ 562
Chapter 110: Genymotion for android
Introduction
Genymotion is a fast third-party emulator that can be used instead of the default Android emulator.
In some cases it's as good as or better than developing on actual devices!
Examples
Installing Genymotion, the free version
Note: you will need to create a new account OR log-in with your account.
Genymotion, can be integrated with Android Studio via a plugin, here the steps to install it in Android
Studio
https://riptutorial.com/ 563
• Select Plugins and click Browse Repositories.
• Right-click on Genymotion and click Download and install.
You should now be able to see the plugin icon, see this image
Note, you might want to display the toolbar by clicking View > Toolbar.
Now you should be able to run Genymotion's emulator by pressing the plugin icon and selecting an
installed emulator and then press start button!
If developers want to test Google Maps or any other Google service like Gmail,Youtube, Google
drive etc. then they first need to install Google framework on Genymotion. Here are the steps:-
4.4 Kitkat
5.0 Lollipop
5.1 Lollipop
6.0 Marshmallow
7.0 Nougat
7.1 Nougat (webview patch)
Reference:-
Stack overflow question on this topic
https://riptutorial.com/ 564
Chapter 111: Gesture Detection
Remarks
Official Documentation: Detecting Common Gestures
Examples
Swipe Detection
@Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float
velocityY) {
float diffY = e2.getY() - e1.getY();
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) >
SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
onSwipeRight();
} else {
onSwipeLeft();
}
}
} else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) >
SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
onSwipeBottom();
} else {
onSwipeTop();
}
}
https://riptutorial.com/ 565
return true;
}
}
Applied to a view...
view.setOnTouchListener(new OnSwipeListener(context) {
public void onSwipeTop() {
Log.d("OnSwipeListener", "onSwipeTop");
}
public void onSwipeRight() {
Log.d("OnSwipeListener", "onSwipeRight");
}
public void onSwipeLeft() {
Log.d("OnSwipeListener", "onSwipeLeft");
}
public void onSwipeBottom() {
Log.d("OnSwipeListener", "onSwipeBottom");
}
});
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGestureDetector = new GestureDetector(this, this);
mGestureDetector.setOnDoubleTapListener(this);
}
@Override
public boolean onTouchEvent(MotionEvent event){
mGestureDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
https://riptutorial.com/ 566
@Override
public boolean onDown(MotionEvent event) {
Log.d("GestureDetector","onDown");
return true;
}
@Override
public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float
velocityY) {
Log.d("GestureDetector","onFling");
return true;
}
@Override
public void onLongPress(MotionEvent event) {
Log.d("GestureDetector","onLongPress");
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
{
Log.d("GestureDetector","onScroll");
return true;
}
@Override
public void onShowPress(MotionEvent event) {
Log.d("GestureDetector","onShowPress");
}
@Override
public boolean onSingleTapUp(MotionEvent event) {
Log.d("GestureDetector","onSingleTapUp");
return true;
}
@Override
public boolean onDoubleTap(MotionEvent event) {
Log.d("GestureDetector","onDoubleTap");
return true;
}
@Override
public boolean onDoubleTapEvent(MotionEvent event) {
Log.d("GestureDetector","onDoubleTapEvent");
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent event) {
Log.d("GestureDetector","onSingleTapConfirmed");
return true;
}
https://riptutorial.com/ 567
Chapter 112: Getting Calculated View
Dimensions
Remarks
Note that a ViewTreeObserver instance associated with a View instance can become invalid while
that View is still alive. From the View.getViewTreeObserver javadocs:
Thus, if you have previously added a listener to a ViewTreeObserver instance and now wish to
remove it, it is easiest to call getViewTreeObserver on the corresponding View instance again to
receive a fresh ViewTreeObserver instance. (Checking isAlive on an existing instance is more work
for little benefit; if the ViewTreeObserver is no longer alive, you'll be fetching that fresh reference
anyway!)
Examples
Calculating initial View dimensions in an Activity
package com.example;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_example);
viewToMeasure.getViewTreeObserver().addOnPreDrawListener(new
ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
// viewToMeasure is now measured and laid out, and displayed dimensions are
known.
https://riptutorial.com/ 568
logComputedViewDimensions(viewToMeasure.getWidth(),
viewToMeasure.getHeight());
https://riptutorial.com/ 569
Chapter 113: Getting started with OpenGL ES
2.0+
Introduction
This topic is about setting up and using OpenGL ES 2.0+ on Android. OpenGL ES is the standard
for 2D and 3D accelerated graphics on embedded systems - including consoles, smartphones,
appliances and vehicles.
Examples
Setting up GLSurfaceView and OpenGL ES 2.0+
To use OpenGL ES in your application you must add this to the manifest:
import static android.opengl.GLES20.*; // To use all OpenGL ES 2.0 methods and constants
statically
https://riptutorial.com/ 570
<com.example.app.MyGLSurfaceView
android:id="@+id/gles_renderer"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
To use newer version of OpenGL ES just change the version number in your manifest, in the static
import and change setEGLContextClientVersion.
The Assets folder is the most common place to store your GLSL-ES shader files. To use them in
your OpenGL ES application you need to load them to a string in the first place. This functions
creates a string from the asset file:
Now you need to create a function that compiles a shader stored in a sting:
https://riptutorial.com/ 571
// Load shaders from file
String vertexShaderString = loadStringFromAssetFile(context, "your_vertex_shader.glsl");
String fragmentShaderString = loadStringFromAssetFile(context, "your_fragment_shader.glsl");
// Compile shaders
int vertexShader = compileShader(GL_VERTEX_SHADER, vertexShaderString);
int fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderString);
glUseProgram(shaderProgram);
https://riptutorial.com/ 572
Chapter 114: Getting system font names and
using the fonts
Introduction
The following examples show how to retrieve the default names of the system fonts that are store
in the /system/fonts/ directory and how to use a system font to set the typeface of a TextView
element.
Examples
Getting system font names
In the following code you need to replace fontsname by the name of the font you would like to use:
Read Getting system font names and using the fonts online:
https://riptutorial.com/android/topic/10930/getting-system-font-names-and-using-the-fonts
https://riptutorial.com/ 573
Chapter 115: Glide
Introduction
**** WARNING This documentation is unmaintained and frequently inaccurate ****
Remarks
Glide is a fast and efficient open source media management and image loading framework for
Android that wraps media decoding, memory and disk caching, and resource pooling into a simple
and easy to use interface.
Glide supports fetching, decoding, and displaying video stills, images, and animated GIFs. Glide
includes a flexible API that allows developers to plug in to almost any network stack.
By default Glide uses a custom HttpUrlConnection based stack, but also includes utility libraries
plug in to Google's Volley project or Square's OkHttp library instead.
Glide's primary focus is on making scrolling any kind of a list of images as smooth and fast as
possible, but Glide is also effective for almost any case where you need to fetch, resize, and
display a remote image.
Examples
Add Glide to your project
With Gradle:
repositories {
mavenCentral() // jcenter() works as well because it pulls from Maven Central
}
dependencies {
compile 'com.github.bumptech.glide:glide:4.0.0'
compile 'com.android.support:support-v4:25.3.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0'
}
With Maven:
https://riptutorial.com/ 574
<dependency>
<groupId>com.github.bumptech.glide</groupId>
<artifactId>glide</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.google.android</groupId>
<artifactId>support-v4</artifactId>
<version>r7</version>
</dependency>
<dependency>
<groupId>com.github.bumptech.glide</groupId>
<artifactId>compiler</artifactId>
<version>4.0.0</version>
<optional>true</optional>
</dependency>
Depending on your ProGuard (DexGuard) config and usage, you may also need to include the
following lines in your proguard.cfg (See Glide's wiki for more info):
Loading an image
ImageView
To load an image from a specified URL, Uri, resource id, or any other model into an ImageView:
Glide.with(context)
.load(yourUrl)
.into(imageView);
For Uris, replace yourUrl with your Uri (content://media/external/images/1). For Drawables replace
yourUrl with your resource id (R.drawable.image).
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
MyViewHolder myViewHolder = (MyViewHolder) viewHolder;
https://riptutorial.com/ 575
String currentUrl = myUrls.get(position);
Glide.with(context)
.load(currentUrl)
.into(myViewHolder.imageView);
}
If you don't want to start a load in onBindViewHolder, make sure you clear() any ImageView Glide
may be managing before modifying the ImageView:
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
MyViewHolder myViewHolder = (MyViewHolder) viewHolder;
String currentUrl = myUrls.get(position);
if (TextUtils.isEmpty(currentUrl)) {
Glide.clear(viewHolder.imageView);
// Now that the view has been cleared, you can safely set your own resource
viewHolder.imageView.setImageResource(R.drawable.missing_image);
} else {
Glide.with(context)
.load(currentUrl)
.into(myViewHolder.imageView);
}
}
https://riptutorial.com/ 576
paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP,
BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
float r = size / 2f;
canvas.drawCircle(r, r, r, paint);
return result;
}
Usage:
Glide.with(context)
.load(yourimageurl)
.transform(new CircleTransform(context))
.into(userImageView);
Default transformations
Glide includes two default transformations, fit center and center crop.
Fit center:
Glide.with(context)
.load(yourUrl)
.fitCenter()
.into(yourView);
Center crop:
Glide.with(context)
.load(yourUrl)
.centerCrop()
.into(yourView);
https://riptutorial.com/ 577
@Override
protected void setResource(final Bitmap resource) {
RoundedBitmapDrawable circularBitmapDrawable =
RoundedBitmapDrawableFactory.create(context.getResources(), resource);
circularBitmapDrawable.setCornerRadius(radius);
imageView.setImageDrawable(circularBitmapDrawable);
}
};
}
Loading image:
Glide.with(context)
.load(imageUrl)
.asBitmap()
.into(UIUtils.getRoundedImageTarget(context, imageView, radius));
Because you use asBitmap() the animations will be removed though. You can use your own
animation in this place using the animate() method.
Glide.with(context)
.load(imageUrl)
.asBitmap()
.animate(R.anim.abc_fade_in)
.into(UIUtils.getRoundedImageTarget(context, imageView, radius));
Please note this animation is support library private resource - it is unrecommended to use as it
can change or even be removed.
Preloading images
To preload remote images and ensure that the image is only downloaded once:
Glide.with(context)
.load(yourUrl)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.preload();
Then:
Glide.with(context)
.load(yourUrl)
.diskCacheStrategy(DiskCacheStrategy.SOURCE) // ALL works here too
.into(imageView);
To preload local images and make sure a transformed copy is in the disk cache (and maybe the
memory cache):
https://riptutorial.com/ 578
Glide.with(context)
.load(yourFilePathOrUri)
.fitCenter() // Or whatever transformation you want
.preload(200, 200); // Or whatever width and height you want
Then:
Glide.with(context)
.load(yourFilePathOrUri)
.fitCenter() // You must use the same transformation as above
.override(200, 200) // You must use the same width and height as above
.into(imageView);
If you want to add a Drawable be shown during the load, you can add a placeholder:
Glide.with(context)
.load(yourUrl)
.placeholder(R.drawable.placeholder)
.into(imageView);
If you want a Drawable to be shown if the load fails for any reason:
Glide.with(context)
.load(yourUrl)
.error(R.drawable.error)
.into(imageView);
If you want a Drawable to be shown if you provide a null model (URL, Uri, file path etc):
Glide.with(context)
.load(maybeNullUrl)
.fallback(R.drawable.fallback)
.into(imageView);
@Override
https://riptutorial.com/ 579
protected void setResource(Bitmap resource)
{
RoundedBitmapDrawable bitmapDrawable =
RoundedBitmapDrawableFactory.create(context.getResources(), resource);
bitmapDrawable.setCircular(true);
imageView.setImageDrawable(bitmapDrawable);
}
}
Usage:
Glide
.with(context)
.load(yourimageidentifier)
.asBitmap()
.into(new CircularBitmapImageViewTarget(context, imageView));
Glide
.with(context)
.load(currentUrl)
.into(new BitmapImageViewTarget(profilePicture) {
@Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable circularBitmapDrawable =
RoundedBitmapDrawableFactory.create(context.getResources(), resource);
circularBitmapDrawable.setCornerRadius(radius);
imageView.setImageDrawable(circularBitmapDrawable);
}
@Override
public void onLoadFailed(@NonNull Exception e, Drawable errorDrawable) {
super.onLoadFailed(e, SET_YOUR_DEFAULT_IMAGE);
Log.e(TAG, e.getMessage(), e);
}
});
Here at SET_YOUR_DEFAULT_IMAGE place you can set any default Drawable. This image will be shown if
Image loading is failed.
https://riptutorial.com/ 580
Chapter 116: Google Awareness APIs
Remarks
Remember, the Snapshot API is used to request current state while the Fence API continuously
checks for a specified state and sends callbacks when an app isn't running.
Overall, there are a few basic steps in order to use the Snapshot API or Fence API:
<!-- Replace with your actual API key from console -->
<meta-data android:name="com.google.android.awareness.API_KEY"
android:value="YOUR_API_KEY"/>
• Parse result
An easy way to check for the needed user permission is a method such as this:
Examples
Get current user activity using Snapshot API
https://riptutorial.com/ 581
For one-time, non-constant requests for a user's physical activity, use the Snapshot API:
https://riptutorial.com/ 582
}
}
});
As for getting the data in those places, here are some options:
If you want to detect when your user starts or finishes an activity such as walking, running, or any
other activity of the DetectedActivityFence class, you can create a fence for the activity that you
want to detect, and get notified when your user starts/finishes this activity. By using a
BroadcastReceiver, you will get an Intent with data that contains the activity:
// Your own action filter, like the ones used in the Manifest.
private static final String FENCE_RECEIVER_ACTION = BuildConfig.APPLICATION_ID +
"FENCE_RECEIVER_ACTION";
private static final String FENCE_KEY = "walkingFenceKey";
private FenceReceiver mFenceReceiver;
private PendingIntent mPendingIntent;
// The 0 is a standard Activity request code that can be changed to your needs.
mPendingIntent = PendingIntent.getBroadcast(this, 0,
new Intent(FENCE_RECEIVER_ACTION), 0);
https://riptutorial.com/ 583
registerReceiver(mFenceReceiver, new IntentFilter(FENCE_RECEIVER_ACTION));
Now you can receive the intent with a BroadcastReceiver to get callbacks when the user changes
the activity:
@Override
public void onReceive(Context context, Intent intent) {
// Get the fence state
FenceState fenceState = FenceState.extract(intent);
switch (fenceState.getCurrentState()) {
case FenceState.TRUE:
Log.i(TAG, "User is walking");
break;
case FenceState.FALSE:
Log.i(TAG, "User is not walking");
break;
case FenceState.UNKNOWN:
Log.i(TAG, "User is doing something unknown");
break;
}
}
}
Get changes for location within a certain range using Fence API
If you want to detect when your user enters a specific location, you can create a fence for the
specific location with a radius you want and be notified when your user enters or leaves the
location.
// Your own action filter, like the ones used in the Manifest
private static final String FENCE_RECEIVER_ACTION = BuildConfig.APPLICATION_ID +
"FENCE_RECEIVER_ACTION";
private static final String FENCE_KEY = "locationFenceKey";
https://riptutorial.com/ 584
private FenceReceiver mFenceReceiver;
private PendingIntent mPendingIntent;
// The 0 is a standard Activity request code that can be changed for your needs
mPendingIntent = PendingIntent.getBroadcast(this, 0,
new Intent(FENCE_RECEIVER_ACTION), 0);
registerReceiver(mFenceReceiver, new IntentFilter(FENCE_RECEIVER_ACTION));
@Override
public void onReceive(Context context, Intent intent) {
// Get the fence state
FenceState fenceState = FenceState.extract(intent);
switch (fenceState.getCurrentState()) {
case FenceState.TRUE:
Log.i(TAG, "User is in location");
break;
case FenceState.FALSE:
Log.i(TAG, "User is not in location");
break;
case FenceState.UNKNOWN:
Log.i(TAG, "User is doing something unknown");
break;
}
}
}
https://riptutorial.com/ 585
Chapter 117: Google Drive API
Introduction
Google Drive is a file hosting service created by Google. It provides file storage service and
allows the user to upload files in the cloud and also share with other people. Using Google Drive
API, we can synchronize files between computer or mobile device and Google Drive Cloud.
Remarks
Legal
If you use the Google Drive Android API in your application, you must include the Google Play
Services attribution text as part of a "Legal Notices" section in your application.
It’s recommended that you include legal notices as an independent menu item, or as part of an
"About" menu item.
Examples
Integrate Google Drive in Android
To integrate Android application with Google Drive, create the credentials of project in the Google
Developers Console. So, we need to create a project on Google Developer console.
• Go to Google Developer Console for Android. Fill your project name in the input field and
click on the create button to create a new project on Google Developer console.
https://riptutorial.com/ 586
• We need to create credentials to access API. So, click on the Create credentials button.
• Now, a pop window will open. Click on API Key option in the list to create API key.
https://riptutorial.com/ 587
• We need an API key to call Google APIs for Android. So, click on the Android Key to
identify your Android Project.
• Next, we need to add Package Name of the Android Project and SHA-1 fingerprint in the
input fields to create API key.
https://riptutorial.com/ 588
• We need to generate SHA-1 fingerprint. So, open your terminal and run Keytool utility to
get the SHA1 fingerprint. While running Keytool utility, you need to provide keystore
password. Default development keytool password is “android”. keytool -exportcert -alias
androiddebugkey -keystore ~/.android/debug.keystore -list -v
https://riptutorial.com/ 589
• Now, add Package name and SHA-1 fingerprint in input fields on credentials page. Finally,
click on create button to create API key.
https://riptutorial.com/ 590
• This will create API key for Android. We will use the this API key to integrate Android
application with Google Drive.
https://riptutorial.com/ 591
Enable Google Drive API
We need to enable Google Drive Api to access files stored on Google Drive from Android
application. To enable Google Drive API, follow below steps:
• Go to your Google Developer console Dashboard and click on Enable APIs get credentials
like keys then you will see popular Google APIs.
https://riptutorial.com/ 592
• Click on Drive API link to open overview page of Google Drive API.
https://riptutorial.com/ 593
• Click on the Enable button to enable Google drive API. It allows client access to Google
Drive.
App needs Internet access Google Drive files. Use the following code to set up Internet
https://riptutorial.com/ 594
permissions in AndroidManifest.xml file :
We will use Google play services API which includes the Google Drive Android API. So, we
need to setup Google play services SDK in Android Application. Open your build.gradle(app
module) file and add Google play services SDK as a dependencies.
dependencies {
....
compile 'com.google.android.gms:play-services:<latest_version>'
....
}
To use Google API in Android application, we need to add API key and version of the Google Play
Service in the AndroidManifest.xml file. Add the correct metadata tags inside the tag of the
AndroidManifest.xml file.
We need to authenticate and connect Google Drive Android API with Android application.
Authorization of Google Drive Android API is handled by the GoogleApiClient. We will use
GoogleApiClient within onResume() method.
/**
* Called when the activity will start interacting with the user.
* At this point your activity is at the top of the activity stack,
* with user input going to it.
*/
@Override
protected void onResume() {
super.onResume();
if (mGoogleApiClient == null) {
/**
* Create the API client and bind it to an instance variable.
* We use this instance as the callback for connection and connection failures.
* Since no account name is passed, the user is prompted to choose.
*/
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
mGoogleApiClient.connect();
}
https://riptutorial.com/ 595
Disconnect Google Deive Android API
When activity stops, we will disconnected Google Drive Android API connection with Android
application by calling disconnect() method inside activity’s onStop() method.
@Override
protected void onStop() {
super.onStop();
if (mGoogleApiClient != null) {
We will implement Connection Callbacks and Connection Failed Listener of Google API client in
MainActivity.java file to know status about connection of Google API client. These listeners provide
onConnected(), onConnectionFailed(), onConnectionSuspended() method to handle the
connection issues between app and Drive.
If user has authorized the application, the onConnected() method is invoked. If user has not
authorized application, onConnectionFailed() method is invoked and a dialog is displayed to user
that your app is not authorized to access Google Drive. In case connection is suspended,
onConnectionSuspended() method is called.
@Override
public void onConnectionFailed(ConnectionResult result) {
if (!result.hasResolution()) {
/**
* The failure has a resolution. Resolve it.
* Called typically when the app is not yet authorized, and an authorization
* dialog is displayed to the user.
*/
try {
result.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
https://riptutorial.com/ 596
} catch (SendIntentException e) {
/**
* It invoked when Google API client connected
* @param connectionHint
*/
@Override
public void onConnected(Bundle connectionHint) {
/**
* It invoked when connection suspended
* @param cause
*/
@Override
public void onConnectionSuspended(int cause) {
We will add a file on Google Drive. We will use the createFile() method of a Drive object to create
file programmatically on Google Drive. In this example we are adding a new text file in the user’s
root folder. When a file is added, we need to specify the initial set of metadata, file contents, and
the parent folder.
We need to create a CreateMyFile() callback method and within this method, use the Drive object
to retrieve a DriveContents resource. Then we pass the API client to the Drive object and call the
driveContentsCallback callback method to handle result of DriveContents.
A DriveContents resource contains a temporary copy of the file's binary stream which is only
available to the application.
We will create a result handler of DriveContents. Within this method, we call the
https://riptutorial.com/ 597
CreateFileOnGoogleDrive() method and pass the result of DriveContentsResult:
/**
* This is the Result result handler of Drive contents.
* This callback method calls the CreateFileOnGoogleDrive() method.
*/
final ResultCallback<DriveContentsResult> driveContentsCallback =
new ResultCallback<DriveContentsResult>() {
@Override
public void onResult(DriveContentsResult result) {
if (result.getStatus().isSuccess()) {
if (fileOperation == true){
CreateFileOnGoogleDrive(result);
}
}
}
};
We use the following code to create a new text file in the user's root folder:
/**
* Create a file in the root folder using a MetadataChangeSet object.
* @param result
*/
public void CreateFileOnGoogleDrive(DriveContentsResult result){
https://riptutorial.com/ 598
.createFile(mGoogleApiClient, changeSet, driveContents)
setResultCallback(fileCallback);
}
}.start();
}
/**
* Handle result of Created file
*/
final private ResultCallback<DriveFolder.DriveFileResult> fileCallback = new
ResultCallback<DriveFolder.DriveFileResult>() {
@Override
public void onResult(DriveFolder.DriveFileResult result) {
if (result.getStatus().isSuccess()) {
Toast.makeText(getApplicationContext(), "file created: "+
result.getDriveFile().getDriveId(), Toast.LENGTH_LONG).show();
}
return;
}
};
https://riptutorial.com/ 599
Chapter 118: Google Maps API v2 for Android
Parameters
Parameter Details
MarkerOptions is the builder class of a Marker, and is used to add one marker
MarkerOptions
to a map.
Remarks
Requirements
Examples
Default Google Map Activity
This Activity code will provide basic functionality for including a Google Map using a
SupportMapFragment.
Activities now have to implement the OnMapReadyCallBack interface, which comes with a
onMapReady() method override that is executed everytime we run SupportMapFragment.
getMapAsync(OnMapReadyCallback); and the call is successfully completed.
Maps use Markers , Polygons and PolyLines to show interactive information to the user.
MapsActivity.java:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
https://riptutorial.com/ 600
}
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
Notice that the code above inflates a layout, which has a SupportMapFragment nested inside the
container Layout, defined with an ID of R.id.map. The layout file is shown below:
activity_maps.xml
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/map"
tools:context="com.example.app.MapsActivity"
android:name="com.google.android.gms.maps.SupportMapFragment"/>
</LinearLayout>
Map Style
Google Maps come with a set of different styles to be applied, using this code :
Normal
map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
Typical road map. Roads, some man-made features, and important natural features such as rivers
are shown. Road and feature labels are also visible.
https://riptutorial.com/ 601
Hybrid
map.setMapType(GoogleMap.MAP_TYPE_HYBRID);
Satellite photograph data with road maps added. Road and feature labels are also visible.
https://riptutorial.com/ 602
Satellite
map.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
Satellite photograph data. Road and feature labels are not visible.
https://riptutorial.com/ 603
Terrain
map.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
Topographic data. The map includes colors, contour lines and labels, and perspective shading.
Some roads and labels are also visible.
https://riptutorial.com/ 604
None
map.setMapType(GoogleMap.MAP_TYPE_NONE);
No tiles. The map will be rendered as an empty grid with no tiles loaded.
https://riptutorial.com/ 605
OTHER STYLE OPTIONS
Indoor Maps
At high zoom levels, the map will show floor plans for indoor spaces. These are called indoor
maps, and are displayed only for the 'normal' and 'satellite' map types.
GoogleMap.setIndoorEnabled(true).
GoogleMap.setIndoorEnabled(false).
mMap = googleMap;
try {
// Customise the styling of the base map using a JSON object defined
// in a raw resource file.
boolean success = mMap.setMapStyle(
MapStyleOptions.loadRawResourceStyle(
MapsActivity.this, R.raw.style_json));
https://riptutorial.com/ 606
if (!success) {
Log.e(TAG, "Style parsing failed.");
}
} catch (Resources.NotFoundException e) {
Log.e(TAG, "Can't find style.", e);
}
under res folder create a folder name raw and add the styles json file. Sample style.json file
[
{
"featureType": "all",
"elementType": "geometry",
"stylers": [
{
"color": "#242f3e"
}
]
},
{
"featureType": "all",
"elementType": "labels.text.stroke",
"stylers": [
{
"lightness": -80
}
]
},
{
"featureType": "administrative",
"elementType": "labels.text.fill",
"stylers": [
{
"color": "#746855"
}
]
},
{
"featureType": "administrative.locality",
"elementType": "labels.text.fill",
"stylers": [
{
"color": "#d59563"
}
]
},
{
"featureType": "poi",
"elementType": "labels.text.fill",
"stylers": [
{
"color": "#d59563"
}
]
},
{
"featureType": "poi.park",
"elementType": "geometry",
"stylers": [
{
https://riptutorial.com/ 607
"color": "#263c3f"
}
]
},
{
"featureType": "poi.park",
"elementType": "labels.text.fill",
"stylers": [
{
"color": "#6b9a76"
}
]
},
{
"featureType": "road",
"elementType": "geometry.fill",
"stylers": [
{
"color": "#2b3544"
}
]
},
{
"featureType": "road",
"elementType": "labels.text.fill",
"stylers": [
{
"color": "#9ca5b3"
}
]
},
{
"featureType": "road.arterial",
"elementType": "geometry.fill",
"stylers": [
{
"color": "#38414e"
}
]
},
{
"featureType": "road.arterial",
"elementType": "geometry.stroke",
"stylers": [
{
"color": "#212a37"
}
]
},
{
"featureType": "road.highway",
"elementType": "geometry.fill",
"stylers": [
{
"color": "#746855"
}
]
},
{
"featureType": "road.highway",
"elementType": "geometry.stroke",
https://riptutorial.com/ 608
"stylers": [
{
"color": "#1f2835"
}
]
},
{
"featureType": "road.highway",
"elementType": "labels.text.fill",
"stylers": [
{
"color": "#f3d19c"
}
]
},
{
"featureType": "road.local",
"elementType": "geometry.fill",
"stylers": [
{
"color": "#38414e"
}
]
},
{
"featureType": "road.local",
"elementType": "geometry.stroke",
"stylers": [
{
"color": "#212a37"
}
]
},
{
"featureType": "transit",
"elementType": "geometry",
"stylers": [
{
"color": "#2f3948"
}
]
},
{
"featureType": "transit.station",
"elementType": "labels.text.fill",
"stylers": [
{
"color": "#d59563"
}
]
},
{
"featureType": "water",
"elementType": "geometry",
"stylers": [
{
"color": "#17263c"
}
]
},
{
https://riptutorial.com/ 609
"featureType": "water",
"elementType": "labels.text.fill",
"stylers": [
{
"color": "#515c6d"
}
]
},
{
"featureType": "water",
"elementType": "labels.text.stroke",
"stylers": [
{
"lightness": -20
}
]
}
]
https://riptutorial.com/ 610
https://riptutorial.com/ 611
Objects, we can do it this way.
Here is a method that would take a list of MyLocation Objects and place a Marker for each one:
Note: For the purpose of this example, mMap is a class member variable of the Activity, where we've
assigned it to the map reference received in the onMapReady() override.
It is possible to treat a GoogleMap as an Android view if we make use of the provided MapView
class. Its usage is very similar to MapFragment.
<com.google.android.gms.maps.MapView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
<!--
map:mapType="0" Specifies a change to the initial map type
map:zOrderOnTop="true" Control whether the map view's surface is placed on top of its
window
map:useVieLifecycle="true" When using a MapFragment, this flag specifies whether the
lifecycle of the map should be tied to the fragment's view or the fragment itself
map:uiCompass="true" Enables or disables the compass
map:uiRotateGestures="true" Sets the preference for whether rotate gestures should be
enabled or disabled
map:uiScrollGestures="true" Sets the preference for whether scroll gestures should be
enabled or disabled
map:uiTiltGestures="true" Sets the preference for whether tilt gestures should be enabled
or disabled
map:uiZoomGestures="true" Sets the preference for whether zoom gestures should be enabled
or disabled
map:uiZoomControls="true" Enables or disables the zoom controls
https://riptutorial.com/ 612
map:liteMode="true" Specifies whether the map should be created in lite mode
map:uiMapToolbar="true" Specifies whether the mapToolbar should be enabled
map:ambientEnabled="true" Specifies whether ambient-mode styling should be enabled
map:cameraMinZoomPreference="0.0" Specifies a preferred lower bound for camera zoom
map:cameraMaxZoomPreference="1.0" Specifies a preferred upper bound for camera zoom -->
/>
/**
* This shows how to create a simple activity with a raw MapView and add a marker to it. This
* requires forwarding all the important lifecycle methods onto MapView.
*/
public class RawMapViewDemoActivity extends AppCompatActivity implements OnMapReadyCallback {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.raw_mapview_demo);
mMapView.getMapAsync(this);
}
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
public void onMapReady(GoogleMap map) {
map.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
}
@Override
protected void onPause() {
mMapView.onPause();
super.onPause();
}
@Override
protected void onDestroy() {
mMapView.onDestroy();
super.onDestroy();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mMapView.onLowMemory();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
https://riptutorial.com/ 613
mMapView.onSaveInstanceState(outState);
}
}
Here is a full Activity class that places a Marker at the current location, and also moves the
camera to the current position.
GoogleMap mGoogleMap;
SupportMapFragment mapFrag;
LocationRequest mLocationRequest;
GoogleApiClient mGoogleApiClient;
Location mLastLocation;
Marker mCurrLocationMarker;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
@Override
public void onPause() {
super.onPause();
@Override
public void onMapReady(GoogleMap googleMap)
{
mGoogleMap=googleMap;
mGoogleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
https://riptutorial.com/ 614
//Initialize Google Play Services
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
//Location Permission already granted
buildGoogleApiClient();
mGoogleMap.setMyLocationEnabled(true);
} else {
//Request Location Permission
checkLocationPermission();
}
}
else {
buildGoogleApiClient();
mGoogleMap.setMyLocationEnabled(true);
}
}
@Override
public void onConnected(Bundle bundle) {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(1000);
mLocationRequest.setFastestInterval(1000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, this);
}
}
@Override
public void onConnectionSuspended(int i) {}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {}
@Override
public void onLocationChanged(Location location)
{
mLastLocation = location;
if (mCurrLocationMarker != null) {
mCurrLocationMarker.remove();
}
https://riptutorial.com/ 615
markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_M
AGENTA));
mCurrLocationMarker = mGoogleMap.addMarker(markerOptions);
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION );
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_LOCATION: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
https://riptutorial.com/ 616
// permission was granted, yay! Do the
// location-related task you need to do.
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
if (mGoogleApiClient == null) {
buildGoogleApiClient();
}
mGoogleMap.setMyLocationEnabled(true);
}
} else {
activity_main.xml:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/map"
tools:context="com.example.app.MapLocationActivity"
android:name="com.google.android.gms.maps.SupportMapFragment"/>
</LinearLayout>
Result:
Show explanation if needed on Marshmallow and Nougat using an AlertDialog (this case happens
when the user had previously denied a permission request, or had granted the permission and
then later revoked it in the settings):
https://riptutorial.com/ 617
Prompt the user for Location permission on Marshmallow and Nougat by calling
ActivityCompat.requestPermissions():
https://riptutorial.com/ 618
Move camera to current location and place Marker when the Location permission is granted:
https://riptutorial.com/ 619
Obtaining the SH1-Fingerprint of your certificate keystore file
In order to obtain a Google Maps API key for your certificate, you must provide the API console
with the SH1-fingerprint of your debug/release keystore.
You can obtain the keystore by using the JDK's keytool program as described here in the docs.
Another approach is to obtain the fingerprint programmatically by running this snippet with your
app signed with the debug/release certificate and printing the hash to the log.
PackageInfo info;
try {
info = getPackageManager().getPackageInfo("com.package.name",
PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md;
md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
String hash= new String(Base64.encode(md.digest(), 0));
Log.e("hash", hash);
}
} catch (NameNotFoundException e1) {
Log.e("name not found", e1.toString());
} catch (NoSuchAlgorithmException e) {
Log.e("no such an algorithm", e.toString());
https://riptutorial.com/ 620
} catch (Exception e) {
Log.e("exception", e.toString());
}
Do not launch Google Maps when the map is clicked (lite mode)
When a Google Map is displayed in lite mode clicking on a map will open the Google Maps
application. To disable this functionality you must call setClickable(false) on the MapView, e.g.:
UISettings
mGoogleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
mGoogleMap.getUiSettings().setMapToolbarEnabled(true);
mGoogleMap.getUiSettings().setZoomControlsEnabled(true);
mGoogleMap.getUiSettings().setCompassEnabled(true);
Result:
https://riptutorial.com/ 621
Get debug SHA1 fingerprint
https://riptutorial.com/ 622
InfoWindow Click Listener
Here is an example of how to define a different action for each Marker's InfoWindow click event.
https://riptutorial.com/ 623
Use a HashMap in which the marker ID is the key, and the value is the corresponding action it
should take when the InfoWindow is clicked.
Then, use a OnInfoWindowClickListener to handle the event of a user clicking the InfoWindow, and
use the HashMap to determine which action to take.
In this simple example we will open up a different Activity based on which Marker's InfoWindow
was clicked.
Then, each time you add a Marker, make an entry in the HashMap with the Marker ID and the
action it should take when it's InfoWindow is clicked.
For example, adding two Markers and defining an action to take for each:
In the InfoWindow click listener, get the action from the HashMap, and open up the corresponding
Activity based on the action of the Marker:
mGoogleMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
@Override
public void onInfoWindowClick(Marker marker) {
if (actionId.equals("action_one")) {
Intent i = new Intent(MainActivity.this, ActivityOne.class);
startActivity(i);
} else if (actionId.equals("action_two")) {
Intent i = new Intent(MainActivity.this, ActivityTwo.class);
startActivity(i);
}
}
});
Change Offset
https://riptutorial.com/ 624
By changing mappoint x and y values as you need you can change offset possition of google
map,by default it will be in the center of the map view. Call below method where you want to
change it! Better to use it inside your onLocationChanged like
changeOffsetCenter(location.getLatitude(),location.getLongitude());
mGoogleMap.animateCamera(CameraUpdateFactory.newLatLng(mGoogleMap.getProjection().fromScreenLocation(ma
https://riptutorial.com/ 625
Chapter 119: Google Play Store
Examples
Open Google Play Store Listing for your app
The following code snippet shows how to open the Google Play Store Listing of your app in a safe
way. Usually you want to use it when asking the user to leave a review for your app.
@SuppressWarnings("deprecation")
private void setFlags(Intent intent) {
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
else
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
}
Note: The code opens the Google Play Store if the app is installed. Otherwise it will just open the
web browser.
Open Google Play Store with the list of all applications from your publisher
account
You can add a "Browse Our Other Apps" button in your app, listing all your(publisher) applications
in the Google Play Store app.
https://riptutorial.com/ 626
}
@SuppressWarnings("deprecation")
public void setFlags(Intent i) {
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
i.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
}
else {
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
}
}
https://riptutorial.com/ 627
Chapter 120: Google signin integration on
android
Introduction
This topic is based on How to integrate google sign-in, On android apps
Examples
Integration of google Auth in your project. (Get a configuration file)
[https://developers.google.com/identity/sign-in/android/start-integrating][1]
• Enter App name And package name and click on choose and configure services
• provide SHA1 Enable google SIGNIN and generate configuration files
Download the configuration file and place the file in app/ folder of your project
classpath 'com.google.gms:google-services:3.0.0'
• In your sign-in activity's onCreate method, configure Google Sign-In to request the user data
required by your app.
• create a GoogleApiClient object with access to the Google Sign-In API and the options you
https://riptutorial.com/ 628
specified.
• Now When User click on Google signin button call this Function.
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
https://riptutorial.com/ 629
Chapter 121: Gradle for Android
Introduction
Gradle is a JVM-based build system that enables developers to write high-level scripts that can be
used to automate the process of compilation and application production. It is a flexible plugin-
based system, which allows you to automate various aspects of the build process; including
compiling and signing a .jar, downloading and managing external dependencies, injecting fields
into the AndroidManifest or utilising specific SDK versions.
Syntax
• apply plugin:The plugins which should been used normally just 'com.android.application' or
'com.android.library'.
Remarks
See also
https://riptutorial.com/ 630
Gradle for Android - Extended documentation:
There is another tag where you can find more topics and examples about the use of gradle in
Android.
http://www.riptutorial.com/topic/2092
Examples
A basic build.gradle file
android {
compileSdkVersion 25
buildToolsVersion '25.0.3'
signingConfigs {
applicationName {
keyAlias 'applicationName'
keyPassword 'password'
storeFile file('../key/applicationName.jks')
storePassword 'keystorePassword'
}
}
defaultConfig {
applicationId 'com.company.applicationName'
minSdkVersion 14
targetSdkVersion 25
versionCode 1
versionName '1.0'
signingConfig signingConfigs.applicationName
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:design:25.3.1'
testCompile 'junit:junit:4.12'
}
https://riptutorial.com/ 631
Each block in the file above is called a DSL (domain-specific language).
Plugins
The first line, apply plugin: 'com.android.application', applies the Android plugin for Gradle to the
build and makes the android {} block available to declare Android-specific build options.
For example, you can set the compileSdkVersion which specifies the Android API level , Which
should be used by Gradle to compile your app.
The sub-block defaultConfig holds the defaults for your manifest. You can override them with
Product Flavors.
Dependencies
The dependencies block is defined outside the android block {...} : This means it's not defined by
the Android plugin but it's standard Gradle.
The dependencies block specifies what external libraries (typically Android libraries, but Java
libraries are also valid) you wish to include in your app. Gradle will automatically download these
dependencies for you (if there is no local copy available), you just need to add similar compile lines
when you wish to add another library.
https://riptutorial.com/ 632
compile 'com.android.support:design:25.3.1'
Gradle will ensure that the library is downloaded and present so that you can use it in your app,
and its code will also be included in your app.
If you're familiar with Maven, this syntax is the GroupId, a colon, ArtifactId, another colon, then the
version of the dependency you wish to include, giving you full control over versioning.
While it is possible to specify artifact versions using the plus (+) sign, best practice is to avoid
doing so; it can lead to issues if the library gets updated with breaking changes without your
knowledge, which would likely lead to crashes in your app.
compile 'com.android.support:appcompat-v7:25.3.1'
This simply means that this library (appcompat) is compatible with the Android API level 7 and
forward.
This is helpful for keeping test- and debug- related dependencies out of your release build, which
will keep your release APK as slim as possible and help to ensure that any debug information
cannot be used to obtain internal information about your app.
https://riptutorial.com/ 633
signingConfig
The signingConfig allows you to configure your Gradle to include keystore information and ensure
that the APK built using these configurations are signed and ready for Play Store release.
Note: It's not recommended though to keep the signing credentials inside your Gradle file. To
remove the signing configurations, just omit the signingConfigs portion.
You can specify them in different ways:
See this topic for more details : Sign APK without exposing keystore password.
You can find further information about Gradle for Android in the dedicated Gradle topic.
Product flavors are defined in the build.gradle file inside the android { ... } block as seen below.
...
android {
...
productFlavors {
free {
applicationId "com.example.app.free"
versionName "1.0-free"
}
paid {
applicationId "com.example.app.paid"
versionName "1.0-paid"
}
}
}
By doing this, we now have two additional product flavors: free and paid. Each can have its own
specific configuration and attributes. For example, both of our new flavors has a separate
applicationId and versionName than our existing main flavor (available by default, so not shown
here).
Dependencies can be added for a specific product flavor, similar to how they can be added for
specific build configurations.
For this example, assume that we have already defined two product flavors called free and paid
(more on defining flavors here).
https://riptutorial.com/ 634
We can then add the AdMob dependency for the free flavor, and the Picasso library for the paid
one like so:
android {
...
productFlavors {
free {
applicationId "com.example.app.free"
versionName "1.0-free"
}
paid {
applicationId "com.example.app.paid"
versionName "1.0-paid"
}
}
}
...
dependencies {
...
// Add AdMob only for free flavor
freeCompile 'com.android.support:appcompat-v7:23.1.1'
freeCompile 'com.google.android.gms:play-services-ads:8.4.0'
freeCompile 'com.android.support:support-v4:23.1.1'
For this example, assume that we have already defined two product flavors called free and paid. In
order to add product flavor-specific resources, we create additional resource folders alongside the
main/res folder, which we can then add resources to like usual. For this example, we'll define a
string, status, for each product flavor:
/src/main/res/values/strings.xml
<resources>
<string name="status">Default</string>
</resources>
/src/free/res/values/strings.xml
<resources>
<string name="status">Free</string>
</resources>
/src/paid/res/values/strings.xml
https://riptutorial.com/ 635
<resources>
<string name="status">Paid</string>
</resources>
The product flavor-specific status strings will override the value for status in the main flavor.
BuildConfigField
Gradle allows buildConfigField lines to define constants. These constants will be accessible at
runtime as static fields of the BuildConfig class. This can be used to create flavors by defining all
fields within the defaultConfig block, then overriding them for individual build flavors as needed.
This example defines the build date and flags the build for production rather than test:
android {
...
defaultConfig {
...
// defining the build date
buildConfigField "long", "BUILD_DATE", System.currentTimeMillis() + "L"
// define whether this build is a production build
buildConfigField "boolean", "IS_PRODUCTION", "false"
// note that to define a string you need to escape it
buildConfigField "String", "API_KEY", "\"my_api_key\""
}
productFlavors {
prod {
// override the productive flag for the flavor "prod"
buildConfigField "boolean", "IS_PRODUCTION", "true"
resValue 'string', 'app_name', 'My App Name'
}
dev {
// inherit default fields
resValue 'string', 'app_name', 'My App Name - Dev'
}
}
}
The defined fields can now be used within the app at runtime by accessing the generated
BuildConfig class:
https://riptutorial.com/ 636
public void example() {
// format the build date
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
String buildDate = dateFormat.format(new Date(BuildConfig.BUILD_DATE));
Log.d("build date", buildDate);
ResValue
The resValue in the productFlavors creates a resource value. It can be any type of resource (string,
dimen, color, etc.). This is similar to defining a resource in the appropriate file: e.g. defining string in
a strings.xml file. The advantage being that the one defined in gradle can be modified based on
your productFlavor/buildVariant. To access the value, write the same code as if you were
accessing a res from the resources file:
getResources().getString(R.string.app_name)
The important thing is that resources defined this way cannot modify existing resources defined in
files. They can only create new resource values.
Some libraries (such as the Google Maps Android API) require an API key provided in the
Manifest as a meta-data tag. If different keys are needed for debugging and production builds,
specify a manifest placeholder filled in by Gradle.
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${MAPS_API_KEY}"/>
android {
defaultConfig {
...
// Your development key
manifestPlaceholders = [ MAPS_API_KEY: "AIza..." ]
}
productFlavors {
prod {
// Your production key
manifestPlaceholders = [ MAPS_API_KEY: "AIza..." ]
}
}
https://riptutorial.com/ 637
}
The Android build system generates a number of fields automatically and places them in
BuildConfig.java. These fields are:
Field Description
a String containing the build type of the application (usually either debug or
BUILD_TYPE
release)
In addition to the above, if you have defined multiple dimensions of flavor then each dimension will
have its own value. For example, if you had two dimensions of flavor for color and size you will
also have the following variables:
Field Description
One recommended way is to separate the Gradle build files, with one build.gradle per module, as
well as one in the project root and another one for the dependencies, for example:
root
+- gradleScript/
| dependencies.gradle
+- module1/
| build.gradle
+- module2/
https://riptutorial.com/ 638
| build.gradle
+- build.gradle
ext {
// Version
supportVersion = '24.1.0'
firebaseVersion = '9.2.0';
firebaseDependencies = [
core: "com.google.firebase:firebase-core:${firebaseVersion}",
database: "com.google.firebase:firebase-database:${firebaseVersion}",
storage: "com.google.firebase:firebase-storage:${firebaseVersion}",
crash: "com.google.firebase:firebase-crash:${firebaseVersion}",
auth: "com.google.firebase:firebase-auth:${firebaseVersion}",
messaging: "com.google.firebase:firebase-messaging:${firebaseVersion}",
remoteConfig: "com.google.firebase:firebase-config:${firebaseVersion}",
invites: "com.google.firebase:firebase-invites:${firebaseVersion}",
adMod: "com.google.firebase:firebase-ads:${firebaseVersion}",
appIndexing: "com.google.android.gms:play-services-
appindexing:${firebaseVersion}",
];
}
Which can then be applied from that file in the top level file build.gradle like so:
// Load dependencies
apply from: 'gradleScript/dependencies.gradle'
Another approach
A less verbose approach for centralizing library dependencies versions can be achieved by
declaring the version number as a variable once, and using it everywhere.
https://riptutorial.com/ 639
In the workspace root build.gradle add this:
ext.v = [
supportVersion:'24.1.1',
]
And in every module that uses the same library add the needed libraries
compile "com.android.support:support-v4:${v.supportVersion}"
compile "com.android.support:recyclerview-v7:${v.supportVersion}"
compile "com.android.support:design:${v.supportVersion}"
compile "com.android.support:support-annotations:${v.supportVersion}"
Different flavors of application builds can contain different resources. To create a flavor-specific
resource make a directory with the lower-case name of your flavor in the src directory and add
your resources in the same way you would normally.
For example, if you had a flavour Development and wanted to provide a distinct launcher icon for it
you would create a directory src/development/res/drawable-mdpi and inside that directory create an
ic_launcher.png file with your development-specific icon.
src/
main/
res/
drawable-mdpi/
ic_launcher.png <-- the default launcher icon
development/
res/
drawable-mdpi/
ic_launcher.png <-- the launcher icon used when the product flavor is 'Development'
(Of course, in this case you would also create icons for drawable-hdpi, drawable-xhdpi etc).
<PROJECT_ROOT>\build.gradleis a "Top-level build file" where you can add configuration options
common to all sub-projects/modules.
If you use another module in your project, as a local library you would have another build.gradle
file: <PROJECT_ROOT>\module\build.gradle
In the top level file you can specify common properties as the buildscript block or some common
properties.
buildscript {
https://riptutorial.com/ 640
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0'
classpath 'com.google.gms:google-services:3.0.0'
}
}
ext {
compileSdkVersion = 23
buildToolsVersion = "23.0.1"
}
In the app\build.gradle you define only the properties for the module:
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
}
dependencies {
//.....
}
A shell script is a very versatile way to extend your build to basically anything you can think of.
As an exmaple, here is a simple script to compile protobuf files and add the result java files to the
source directory for further compilation:
def compilePb() {
exec {
// NOTICE: gradle will fail if there's an error in the protoc file...
executable "../pbScript.sh"
}
}
project.afterEvaluate {
compilePb()
}
The 'pbScript.sh' shell script for this example, located in the project's root folder:
#!/usr/bin/env bash
pp=/home/myself/my/proto
/usr/local/bin/protoc -I=$pp \
--java_out=./src/main/java \
--proto_path=$pp \
$pp/my.proto \
https://riptutorial.com/ 641
--proto_path=$pp \
$pp/my_other.proto
The following is an excerpt from Gradle - What is a non-zero exit value and how do I fix it?, see it
for the full discussion.
Let's say you are developing an application and you get some Gradle error that appears that
generally will look like so.
:module:someTask FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':module:someTask'.
> some message here... finished with non-zero exit value X
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get
more log output.
BUILD FAILED
Total time: Y.ZZ secs
You search here on StackOverflow for your problem, and people say to clean and rebuild your
project, or enable MultiDex, and when you try that, it just isn't fixing the problem.
There are ways to get more information, but the Gradle output itself should point at the actual error
in the few lines above that message between :module:someTask FAILED and the last
:module:someOtherTask that passed. Therefore, if you ask a question about your error, please edit
your questions to include more context to the error.
So, you get a "non-zero exit value." Well, that number is a good indicator of what you should try to
fix. Here are a few occur most frequently.
• 1 is a just a general error code and the error is likely in the Gradle output
• 2 seems to be related to overlapping dependencies or project misconfiguration.
• 3 seems to be from including too many dependencies, or a memory issue.
The general solutions for the above (after attempting a Clean and Rebuild of the project) are:
• 1- Address the error that is mentioned. Generally, this is a compile-time error, meaning
some piece of code in your project is not valid. This includes both XML and Java for an
Android project.
• 2 & 3 - Many answers here tell you to enable multidex. While it may fix the problem, it is most
likely a workaround. If you don't understand why you are using it (see the link), you probably
don't need it. General solutions involve cutting back your overuse of library dependencies
(such as all of Google Play Services, when you only need to use one library, like Maps or
Sign-In, for example).
Specifying different application IDs for build types and product flavors
https://riptutorial.com/ 642
You can specify different application IDs or package names for each buildType or productFlavor
using the applicationIdSuffix configuration attribute:
defaultConfig {
applicationId "com.package.android"
minSdkVersion 17
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
debuggable false
}
development {
debuggable true
applicationIdSuffix ".dev"
}
testing {
debuggable true
applicationIdSuffix ".qa"
}
}
productFlavors {
free {
applicationIdSuffix ".free"
}
paid {
applicationIdSuffix ".paid"
}
}
You can define the signing configuration to sign the apk in the build.gradle file using these
properties:
https://riptutorial.com/ 643
• storeFile : the keystore file
• storePassword: the keystore password
• keyAlias: a key alias name
• keyPassword: A key alias password
In many case you may need to avoid this kind of info in the build.gradle file.
First, create a file called keystore.properties in the root of your project with content like this
(replacing the values with your own):
storeFile=keystore.jks
storePassword=storePassword
keyAlias=keyAlias
keyPassword=keyPassword
Now, in your app's build.gradle file, set up the signingConfigs block as follows:
android {
...
signingConfigs {
release {
def propsFile = rootProject.file('keystore.properties')
if (propsFile.exists()) {
def props = new Properties()
props.load(new FileInputStream(propsFile))
storeFile = file(props['storeFile'])
storePassword = props['storePassword']
keyAlias = props['keyAlias']
keyPassword = props['keyPassword']
}
}
}
}
That's really all there is to it, but don't forget to exclude both your keystore file and your
https://riptutorial.com/ 644
keystore.properties file from version control.
• The storeFile path specified in the keystore.properties file should be relative to your app's
build.gradle file. This example assumes that the keystore file is in the same directory as the
app's build.gradle file.
• This example has the keystore.properties file in the root of the project. If you put it
somewhere else, be sure to change the value in rootProject.file('keystore.properties') to
the location of yours, relative to the root of your project.
android {
signingConfigs {
release {
storeFile file('/your/keystore/location/key')
keyAlias 'your_alias'
String ps = System.getenv("ps")
if (ps == null) {
throw new GradleException('missing ps env variable')
}
keyPassword ps
storePassword ps
}
}
The "ps" environment variable can be global, but a safer approach can be by adding it to the shell
of Android Studio only.
In linux this can be done by editing Android Studio's Desktop Entry
You can use Gradle to auto-increment your package version each time you build it. To do so
create a version.properties file in the same directory as your build.gradle with the following
contents:
VERSION_MAJOR=0
VERSION_MINOR=1
VERSION_BUILD=1
(Changing the values for major and minor as you see fit). Then in your build.gradle add the
https://riptutorial.com/ 645
following code to the android section:
versionProps.load(new FileInputStream(versionPropsFile))
defaultConfig {
versionCode versionBuild
versionName "${versionMajor}.${versionMinor}." + String.format("%05d", versionBuild)
}
}
The information can be accessed in Java as a string BuildConfig.VERSION_NAME for the complete
{major}.{minor}.{build} number and as an integer BuildConfig.VERSION_CODE for just the build
number.
This is the code for changing output application file name (.apk). The name can be configured by
assigning a different value to newName
android {
https://riptutorial.com/ 646
Disable image compression for a smaller APK file size
If you are optimizing all images manually, disable APT Cruncher for a smaller APK file size.
android {
aaptOptions {
cruncherEnabled = false
}
}
For enabling Proguard configurations for your application you need to enable it in your module-
level gradle file. You need to set the value of minifyEnabled to true.
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
The above code will apply your Proguard configurations contained in the default Android SDK
combined with the "proguard-rules.pro" file on your module to your released apk.
Enable and configure the experimental Gradle plugin to improve AndroidStudio's NDK support.
Check that you fulfill the following requirements:
classpath 'com.android.tools.build:gradle:2.1.2'
to
classpath 'com.android.tools.build:gradle-experimental:0.7.2'
(v0.7.2 was the latest version at the time of writing. Check the latest version yourself and adapt
your line accordingly)
https://riptutorial.com/ 647
The build.gradle file should look similar to this:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle-experimental:0.7.2'
}
}
allprojects {
repositories {
jcenter()
}
}
model {
android {
compileSdkVersion 19
buildToolsVersion "24.0.1"
defaultConfig {
applicationId "com.example.mydomain.myapp"
minSdkVersion.apiLevel 19
targetSdkVersion.apiLevel 19
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles.add(file('proguard-android.txt'))
}
}
ndk {
moduleName "myLib"
https://riptutorial.com/ 648
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
Sync and check that there are no errors in the Gradle files before proceeding.
The getString() part should be highlighted red saying that the corresponding JNI function could
not be found. Hover your mouse over the function call until a red lightbulb appears. Click the bulb
and select create function JNI_.... This should generate a myLib.c file in the
myApp/app/src/main/jni directory with the correct JNI function call. It should look similar to this:
#include <jni.h>
If it doesn't look like this, then the plugin has not correctly been configured or the NDK has not
been downloaded
Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project.
https://riptutorial.com/ 649
signingReport - Displays the signing info for each variant.
sourceSets - Prints out all the source sets defined in this project.
Build tasks
-----------
assemble - Assembles all variants of all applications and secondary packages.
assembleAndroidTest - Assembles all the Test applications.
assembleDebug - Assembles all Debug builds.
assembleRelease - Assembles all Release builds.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
compileDebugAndroidTestSources
compileDebugSources
compileDebugUnitTestSources
compileReleaseSources
compileReleaseUnitTestSources
extractDebugAnnotations - Extracts Android annotations for the debug variant into the archive
file
extractReleaseAnnotations - Extracts Android annotations for the release variant into the
archive file
jar - Assembles a jar archive containing the main classes.
mockableAndroidJar - Creates a version of android.jar that's suitable for unit tests.
testClasses - Assembles test classes.
Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.
Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project
'LeitnerBoxPro'.
components - Displays the components produced by root project 'LeitnerBoxPro'. [incubating]
dependencies - Displays all dependencies declared in root project 'LeitnerBoxPro'.
dependencyInsight - Displays the insight into a specific dependency in root project
'LeitnerBoxPro'.
help - Displays a help message.
model - Displays the configuration model of root project 'LeitnerBoxPro'. [incubating]
projects - Displays the sub-projects of root project 'LeitnerBoxPro'.
properties - Displays the properties of root project 'LeitnerBoxPro'.
tasks - Displays the tasks runnable from root project 'LeitnerBoxPro' (some of the displayed
tasks may belong to subprojects)
.
Install tasks
-------------
installDebug - Installs the Debug build.
installDebugAndroidTest - Installs the android (on device) tests for the Debug build.
uninstallAll - Uninstall all applications.
uninstallDebug - Uninstalls the Debug build.
uninstallDebugAndroidTest - Uninstalls the android (on device) tests for the Debug build.
uninstallRelease - Uninstalls the Release build.
https://riptutorial.com/ 650
Verification tasks
------------------
check - Runs all checks.
connectedAndroidTest - Installs and runs instrumentation tests for all flavors on connected
devices.
connectedCheck - Runs all device checks on currently connected devices.
connectedDebugAndroidTest - Installs and runs the tests for debug on connected devices.
deviceAndroidTest - Installs and runs instrumentation tests using all Device Providers.
deviceCheck - Runs all device checks using Device Providers and Test Servers.
lint - Runs lint on all variants.
lintDebug - Runs lint on the Debug build.
lintRelease - Runs lint on the Release build.
test - Run unit tests for all variants.
testDebugUnitTest - Run unit tests for the debug build.
testReleaseUnitTest - Run unit tests for the release build.
Other tasks
-----------
assembleDefault
clean
jarDebugClasses
jarReleaseClasses
transformResourcesWithMergeJavaResForDebugUnitTest
transformResourcesWithMergeJavaResForReleaseUnitTest
If you don't need automatically generated apk files with unaligned suffix (which you probably don't),
you may add the following code to build.gradle file:
From here
For some reasons you may want to ignore your build variants. For example: you have 'mock'
product flavour and you use it only for debug purposes, such as unit/instrumentation tests.
Let's ignore mockRelease variant from our project. Open build.gradle file and write:
https://riptutorial.com/ 651
// Remove mockRelease as it's not needed.
android.variantFilter { variant ->
if (variant.buildType.name.equals('release') &&
variant.getFlavors().get(0).name.equals('mock')) {
variant.setIgnore(true);
}
}
Use the task dependencies. Depending on how your modules are set up, it may be either
./gradlew dependencies or to see the dependencies of module app use ./gradlew :app:dependencies
dependencies {
compile 'com.android.support:design:23.2.1'
compile 'com.android.support:cardview-v7:23.1.1'
compile 'com.google.android.gms:play-services:6.5.87'
}
------------------------------------------------------------
Project :app
------------------------------------------------------------
. . .
_releaseApk - ## Internal use, do not manually configure ##
+--- com.android.support:design:23.2.1
| +--- com.android.support:support-v4:23.2.1
| | \--- com.android.support:support-annotations:23.2.1
| +--- com.android.support:appcompat-v7:23.2.1
| | +--- com.android.support:support-v4:23.2.1 (*)
| | +--- com.android.support:animated-vector-drawable:23.2.1
| | | \--- com.android.support:support-vector-drawable:23.2.1
| | | \--- com.android.support:support-v4:23.2.1 (*)
| | \--- com.android.support:support-vector-drawable:23.2.1 (*)
| \--- com.android.support:recyclerview-v7:23.2.1
| +--- com.android.support:support-v4:23.2.1 (*)
| \--- com.android.support:support-annotations:23.2.1
+--- com.android.support:cardview-v7:23.1.1
\--- com.google.android.gms:play-services:6.5.87
\--- com.android.support:support-v4:21.0.0 -> 23.2.1 (*)
. . .
Here you can see the project is directly including com.android.support:design version 23.2.1, which
itself is bringing com.android.support:support-v4 with version 23.2.1. However,
com.google.android.gms:play-services itself has a dependency on the same support-v4 but with an
older version 21.0.0, which is a conflict detected by gradle.
(*)
https://riptutorial.com/ 652
are used when gradle skips the subtree because those dependencies were already listed
previously.
root
+- module1/
| build.gradle
+- module2/
| build.gradle
+- build.gradle
+- gradle.properties
usage in a submodule
defaultConfig {
// appXXX are defined in gradle.properties
versionCode = Long.valueOf(appVersionCode)
versionName = appVersionName
}
}
dependencies {
...
}
Note: If you want to publish your app in the F-Droid app store you have to use magic numbers in
the gradle file because else f-droid robot cannot read current versionnumner to detect/verify
https://riptutorial.com/ 653
version changes.
In some circumstances (for example obtaining a Google API key) you need to find your keystore
fingerprint. Gradle has a convenient task that display all the signing information, including keystore
fingerprints:
./gradlew signingReport
:app:signingReport
Variant: release
Config: none
----------
Variant: debug
Config: debug
Store: /Users/user/.android/debug.keystore
Alias: AndroidDebugKey
MD5: 25:08:76:A9:7C:0C:19:35:99:02:7B:00:AA:1E:49:CA
SHA1: 26:BE:89:58:00:8C:5A:7D:A3:A9:D3:60:4A:30:53:7A:3D:4E:05:55
Valid until: Saturday 18 June 2044
----------
Variant: debugAndroidTest
Config: debug
Store: /Users/user/.android/debug.keystore
Alias: AndroidDebugKey
MD5: 25:08:76:A9:7C:0C:19:35:99:02:7B:00:AA:1E:49:CA
SHA1: 26:BE:89:58:00:8C:5A:7D:A3:A9:D3:60:4A:30:53:7A:3D:4E:05:55
Valid until: Saturday 18 June 2044
----------
Variant: debugUnitTest
Config: debug
Store: /Users/user/.android/debug.keystore
Alias: AndroidDebugKey
MD5: 25:08:76:A9:7C:0C:19:35:99:02:7B:00:AA:1E:49:CA
SHA1: 26:BE:89:58:00:8C:5A:7D:A3:A9:D3:60:4A:30:53:7A:3D:4E:05:55
Valid until: Saturday 18 June 2044
----------
Variant: releaseUnitTest
Config: none
----------
You can create and configure build types in the module-level build.gradle file inside the android {}
block.
android {
...
defaultConfig {...}
buildTypes {
release {
https://riptutorial.com/ 654
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-
rules.pro'
}
debug {
applicationIdSuffix ".debug"
}
}
}
https://riptutorial.com/ 655
Chapter 122: GreenDAO
Introduction
GreenDAO is an Object-Relational Mapping library to help developers use SQLite databases for
persistent local storage.
Examples
Helper methods for SELECT, INSERT, DELETE, UPDATE queries
This example shows a helper class that contains methods useful, when executing the queries for
data. Every method here uses Java Generic's in order to be very flexible.
https://riptutorial.com/ 656
return items != null && items.size() > 0 ? items.get(0) : null;
}
https://riptutorial.com/ 657
return qb.list();
}
Creating an Entity with GreenDAO 3.X that has a Composite Primary Key
When creating a model for a table that has a composite primary key, additional work is required on
the Object for the model Entity to respect those constraints.
The following example SQL table and Entity demonstrates the structure to store a review left by a
customer for an item in an online store. In this example, we want the customer_id and item_id
columns to be a composite primary key, allowing only one review to exist between a specific
customer and item.
https://riptutorial.com/ 658
SQL Table
Usually we would use the @Id and @Unique annotations above the respective fields in the entity
class, however for a composite primary key we do the following:
1. Add the @Index annotation inside the class-level @Entity annotation. The value property
contains a comma-delimited list of the fields that make up the key. Use the unique property
as shown to enforce uniqueness on the key.
2. GreenDAO requires every Entity have a long or Long object as a primary key. We still need to
add this to the Entity class, however we do not need to use it or worry about it affecting our
implementation. In the example below it is called localID
Entity
@Id(autoincrement = true)
private Long localID;
@NotNull
private Integer star_rating;
public Review() {}
}
After adding the GreenDao library dependency and Gradle plugin, we need to first create an entity
object.
Entity
An entity is a Plain Old Java Object (POJO) that models some data in the database. GreenDao
will use this class to create a table in the SQLite database and automatically generate helper
classes we can use to access and store data without having to write SQL statements.
@Entity
public class Users {
https://riptutorial.com/ 659
@Id(autoincrement = true)
private Long id;
@Unique
private String email;
After the entity object is created, GreenDao automatically creates the helper classes used to
interact with the database. These are named similarly to the name of the entity object that was
created, followed by Dao and are retrieved from the daoSession object.
Many typical database actions can now be performed using this Dao object with the entity object.
Query
Insert
Update
https://riptutorial.com/ 660
// Modify a previously retrieved user object and update
user.setLastname("Dole");
usersDao.update(user);
Delete
https://riptutorial.com/ 661
Chapter 123: GreenRobot EventBus
Syntax
• @Subscribe(threadMode = ThreadMode.POSTING) public void onEvent(EventClass event) {
}
Parameters
Will be called on the same thread that the event was posted on. This
ThreadMode.POSTING
is the default mode.
Examples
Creating an Event object
For sending and receiving events we first need an Event object. Event objects are actually simple
POJOs.
Receiving Events
For receiving events you need to register your class on the EventBus.
https://riptutorial.com/ 662
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void handleEvent(ArbitaryEvent event) {
Toast.makeText(getActivity(), "Event type: "+event.getEventType(),
Toast.LENGTH_SHORT).show();
}
Sending Events
Sending events is as easy as creating the Event object and then posting it.
EventBus.getDefault().post(new ArbitaryEvent(ArbitaryEvent.TYPE_1));
The first thing we need to do it add EventBus to our module's gradle file:
dependencies {
...
compile 'org.greenrobot:eventbus:3.0.0'
...
}
Now we need to create a model for our event. It can contain anything we want to pass along. For
now we'll just make an empty class.
Now we can add the code to our Activity that will register with EventBus and subscribe to the
event.
@Override
protected void onCreate (Bundle savedInstanceState)
https://riptutorial.com/ 663
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
_eventBus = EventBus.getDefault();
}
@Override
protected void onStart ()
{
super.onStart();
_eventBus.register(this);
}
@Override
protected void onStop ()
{
_eventBus.unregister(this);
super.onStop();
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onDeviceConnected (final DeviceConnectedEvent event)
{
// Process event and update UI
}
}
In this Activity we get an instance of EventBus in the onCreate() method. We register / unregister
for events in onStart() / onStop(). It's important to remember to unregister when your listener loses
scope or you could leak your Activity.
Finally we define the method that we want called with the event. The @Subscribe annotation tells
EventBus which methods it can look for to handle events. You have to have at least one methods
annotated with @Subscribe to register with EventBus or it will throw an exception. In the annotation
we define the thread mode. This tells EventBus which thread to call the method on. It is a very
handy way of passing information from a background thread to the UI thread! That's exactly what
we're doing here. ThreadMode.MAIN means that this method will be called on Android's main UI
thread so it's safe to do any UI manipulations here that you need. The name of the method doesn't
matter. The only think, other that the @Subscribe annotation, that EventBus is looking for is the type
of the argument. As long as the type matches it will be called when an event is posted.
The last thing we need to do it to post an event. This code will be in our Service.
EventBus.getDefault().post(new DeviceConnectedEvent());
That's all there is to it! EventBus will take that DeviceConnectedEvent and look through its
registered listeners, look through the methods that they've subscribed and find the ones that take
a DeviceConnectedEvent as an argument and call them on the thread that they want to be called
on.
https://riptutorial.com/ 664
Chapter 124: Gson
Introduction
Gson is a Java library that can be used to convert Java Objects into their JSON representation.
Gson considers both of these as very important design goals.
Gson Features:
Provide simple toJson() and fromJson() methods to convert Java objects to JSON and vice-versa
Support arbitrarily complex objects (with deep inheritance hierarchies and extensive use of generic
types)
Syntax
• Excluder excluder()
• FieldNamingStrategy fieldNamingStrategy()
• <T> T fromJson(JsonElement json, Class<T> classOfT)
• <T> T fromJson(JsonElement json, Type typeOfT)
• <T> T fromJson(JsonReader reader, Type typeOfT)
• <T> T fromJson(Reader json, Class<T> classOfT)
• <T> T fromJson(Reader json, Type typeOfT)
• <T> T fromJson(String json, Class<T> classOfT)
• <T> T fromJson(String json, Type typeOfT)
• <T> TypeAdapter<T> getAdapter(Class<T> type)
• <T> TypeAdapter<T> getAdapter(TypeToken<T> type)
• <T> TypeAdapter<T> getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken<T>
type)
• JsonReader newJsonReader(Reader reader)
• JsonWriter newJsonWriter(Writer writer)
• JsonElement toJsonTree(Object src)
• JsonElement toJsonTree(Object src, Type typeOfSrc)
• boolean serializeNulls()
• boolean htmlSafe()
• String toJson(JsonElement jsonElement)
• String toJson(Object src)
• String toJson(Object src, Type typeOfSrc)
• String toString()
• void toJson(Object src, Type typeOfSrc, Appendable writer)
• void toJson(Object src, Type typeOfSrc, JsonWriter writer)
https://riptutorial.com/ 665
• void toJson(JsonElement jsonElement, Appendable writer)
• void toJson(JsonElement jsonElement, JsonWriter writer)
• void toJson(Object src, Appendable writer)
Examples
Parsing JSON with Gson
The example shows parsing a JSON object using the Gson library from Google.
Parsing objects:
class Robot {
//OPTIONAL - this annotation allows for the key to be different from the field name, and
can be omitted if key and field name are same . Also this is good coding practice as it
decouple your variable names with server keys name
@SerializedName("version")
private String version;
@SerializedName("age")
private int age;
@SerializedName("robotName")
private String name;
// optional : Benefit it allows to set default values and retain them, even if key is
missing from Json response. Not required for primitive data types.
public Robot{
version = "";
name = "";
}
Parsing a list:
When retrieving a list of JSON objects, often you will want to parse them and convert them into
Java objects.
https://riptutorial.com/ 666
{
"owned_dogs": [
{
"name": "Ron",
"age": 12,
"breed": "terrier"
},
{
"name": "Bob",
"age": 4,
"breed": "bulldog"
},
{
"name": "Johny",
"age": 3,
"breed": "golden retriever"
}
]
}
This particular JSON array contains three objects. In our Java code we'll want to map these
objects to Dog objects. A Dog object would look like this:
@SerializedName("breed")
public String breedName;
}
The Type object typeListOfDogs defines what a list of Dog objects would look like. GSON can use
this type object to map the JSON array to the right values.
https://riptutorial.com/ 667
If you want to parse a String to enum with Gson:
{"status" : "open"}
Method 1
This is useful for most generic container classes, since you can't get the class of a parameterized
type (ie: you can't call List<String>.class).
Method 2
...
Alternatively, you can always subclass the type you want, and then pass in that class. However
this isn't always best practice, since it will return to you an object of type StringList;
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
apt 'com.google.auto.value:auto-value:1.2'
apt 'com.ryanharter.auto.value:auto-value-gson:0.3.1'
provided 'com.jakewharton.auto.value:auto-value-annotations:1.2-update1'
provided 'org.glassfish:javax.annotation:10.0-b28'
https://riptutorial.com/ 668
Create object with autovalue:
Deserialize
Serialize
Using Gson is a great way to simplify Serialization and Deserialization code by using POJO
objects. The side effect is that reflection is costly performance wise. That's why using AutoValue-
Gson to generate CustomTypeAdapter will avoid this reflection cost while staying very simple to
update when an api change is happening.
["first","second","third"]
https://riptutorial.com/ 669
But if we want parse it into a List<String> object, we must use TypeToken.
This example shows how to parse this JSON string to the related generic class object:
dependencies {
compile 'com.google.code.gson:gson:2.8.1'
}
The below line will compile latest version of gson library everytime you compile, you do not have to
change version.
Pros: You can use latest features, speed and less bugs.
Cons: It might break compatibility with your code.
compile 'com.google.code.gson:gson:+'
https://riptutorial.com/ 670
Using Gson to load a JSON file from disk.
This will load a JSON file from disk and convert it to the given type.
Sometimes you need to serialize or deserialize some fields in a desired format, for example your
backend may use the format "YYYY-MM-dd HH:mm" for dates and you want your POJOS to use
the DateTime class in Joda Time.
In order to automatically convert these strings into DateTimes object, you can use a custom
converter.
/**
* Gson serialiser/deserialiser for converting Joda {@link DateTime} objects.
*/
public class DateTimeConverter implements JsonSerializer<DateTime>, JsonDeserializer<DateTime>
{
@Inject
public DateTimeConverter() {
this.dateTimeFormatter = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm");
}
@Override
public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext
context) {
return new JsonPrimitive(dateTimeFormatter.print(src));
}
@Override
public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext
context)
throws JsonParseException {
return dateTimeFormatter.parseDateTime(json.getAsString());
}
}
To make Gson use the newly created converter you need to assign it when creating the Gson
object:
https://riptutorial.com/ 671
DateTimeConverter dateTimeConverter = new DateTimeConverter();
Gson gson = new GsonBuilder().registerTypeAdapter(DateTime.class, dateTimeConverter)
.create();
String s = gson.toJson(DateTime.now());
// this will show the date in the desired format
In order to deserialize the date in that format you only have to define a field in the DateTime
format:
When Gson encounters a field of type DateTime, it will call your converter in order to deserialize
the field.
First of all you need to add the GsonConverterFactory to your build.gradle file
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
Then, you have to add the converter factory when creating the Retrofit Service:
You can add custom converters when creating the Gson object that you are passing to the factory.
Allowing you to create custom type conversions.
{
"total_count": 132,
"page_size": 2,
"page_index": 1,
"twitter_posts": [
{
"created_on": 1465935152,
"tweet_id": 210462857140252672,
"tweet": "Along with our new #Twitterbird, we've also updated our Display Guidelines",
"url": "https://twitter.com/twitterapi/status/210462857140252672"
},
{
"created_on": 1465995741,
https://riptutorial.com/ 672
"tweet_id": 735128881808691200,
"tweet": "Information on the upcoming changes to Tweets is now on the developer site",
"url": "https://twitter.com/twitterapi/status/735128881808691200"
}
]
}
We can parse this array into a Custom Tweets (tweets list container) object manually, but it is
easier to do it with fromJson method:
class Tweets {
@SerializedName("total_count")
int totalCount;
@SerializedName("page_size")
int pageSize;
@SerializedName("page_index")
int pageIndex;
// all you need to do it is just define List variable with correct name
@SerializedName("twitter_posts")
List<Tweet> tweets;
}
class Tweet {
@SerializedName("created_on")
long createdOn;
@SerializedName("tweet_id")
String tweetId;
@SerializedName("tweet")
String tweetBody;
@SerializedName("url")
String url;
}
and if you need just parse a json array you can use this code in your parsing:
Imagine you have all dates in all responses in some custom format, for instance
/Date(1465935152)/ and you want apply general rule to deserialize all Json dates to java Date
instances. In this case you need to implement custom Json Deserializer.
Example of json:
https://riptutorial.com/ 673
"id": 1,
"created_on": "Date(1465935152)",
"updated_on": "Date(1465968945)",
"name": "Oleksandr"
}
class User {
@SerializedName("id")
long id;
@SerializedName("created_on")
Date createdOn;
@SerializedName("updated_on")
Date updatedOn;
@SerializedName("name")
String name;
}
Custom deserializer:
@Override
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext
context) throws JsonParseException {
String dateString = json.getAsString();
if (dateString.startsWith(DATE_PREFIX) && dateString.endsWith(DATE_SUFFIX)) {
dateString = dateString.substring(DATE_PREFIX.length(), dateString.length() -
DATE_SUFFIX.length());
} else {
throw new JsonParseException("Wrong date format: " + dateString);
}
return new Date(Long.parseLong(dateString) - TimeZone.getDefault().getRawOffset());
}
}
This also applies to the case where you want to make Gson Date conversion compatible with
Jackson, for example.
Jackson usually serializes Date to "milliseconds since epoch" whereas Gson uses a readable
format like Aug 31, 2016 10:26:17 to represent Date. This leads to JsonSyntaxExceptions in Gson
when you try to deserialize a Jackson format Date.
https://riptutorial.com/ 674
To circumvent this, you can add a custom serializer and a custom deserializer:
@Override
public int getInt() {
return b;
}
}
@Override
public int getInt() {
return c;
}
}
https://riptutorial.com/ 675
DerivedClass1 derivedClass1 = new DerivedClass1();
derivedClass1.b = 5;
derivedClass1.a = 10;
Now, in another place, we receive this json string and want to deserialize it - but in compile time
we only know it is supposed to be an instance of BaseClass:
But GSON does not know derivedClass1Json was originally an instance of DerivedClass1, so this will
print out 10.
You need to build your own JsonDeserializer, that handles such cases. The solution is not
perfectly clean, but I could not come up with a better one.
@SerializedName("type")
private String typeName;
public BaseClass() {
typeName = getClass().getName();
}
@Override
public T deserialize(
JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
JsonPrimitive classNamePrimitive = (JsonPrimitive) jsonObject.get("type");
Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new JsonParseException(e.getMessage());
}
return context.deserialize(jsonObject, clazz);
}
}
https://riptutorial.com/ 676
All there is left to do is hook everything up -
https://riptutorial.com/ 677
Chapter 125: Handler
Remarks
A Handler can be easily used to execute code after a delayed amount of time. It is also useful for
executing code repeatedly after a specified amount of time by calling the Handler.postDelayed()
method again from within the Runnable's run() method.
Examples
Using a Handler to execute code after a delayed amount of time
As Handlers are used to send Messages and Runnables to a Thread's message queue it's easy to
implement event based communication between multiple Threads. Every Thread that has a Looper
is able to receive and process messages. A HandlerThread is a Thread that implements such a
Looper, for example the main Thread (UI Thread) implements the features of a HandlerThread.
https://riptutorial.com/ 678
Handler handler = new Handler(Looper.getMainLooper());
To stop the Handler from execution remove the callback attached to it using the runnable running
inside it:
https://riptutorial.com/ 679
}
This can be useful if you're writing a game or something that needs to execute a piece of code
every a few seconds.
import android.os.Handler;
https://riptutorial.com/ 680
if (started)
startTimer ();
}
}
Example usage:
https://riptutorial.com/ 681
Chapter 126: Handling Deep Links
Introduction
Deep links are URLs that take users directly to specific content in your app. You can set up deep
links by adding intent filters and extracting data from incoming intents to drive users to the right
screen in your app.
Parameters
<data>
Details
Attribute
scheme The scheme part of a URI (case-sensitive). Examples: http, https, ftp
path The path part of a URI. Must begin with /. Examples: /, /about
pathPrefix A prefix for the path part of a URI. Examples: /item, /article
pathPattern A pattern to match for the path part of a URI. Examples: /item/.*,
/article/[0-9]*
Remarks
The <intent-filter>
This combination of <action> and <category> elements is what tells the Android system that a
specific Activity should be launched when the user clicks on a link in another application.
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
https://riptutorial.com/ 682
The set of deep links that your <intent-filter> supports is the cross-product of all the <data>
elements that you define in that intent-filter. The multiple domain, multiple path, and multiple
scheme examples demonstrate this.
Resources
• Enabling Deep Links for App Content (developer.android.com)
• <intent-filter> (developer.android.com
Examples
Simple deep link
AndroidManifest.xml:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http"
android:host="www.example.com" />
</intent-filter>
</activity>
This will accept any link starting with http://www.example.com as a deep link to start your
MainActivity.
AndroidManifest.xml:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http"
android:host="www.example.com" />
</intent-filter>
https://riptutorial.com/ 683
</activity>
This will launch your MainActivity when the user clicks any of these links:
• http://www.example.com/
• http://www.example.com/about
• http://www.example.com/map
AndroidManifest.xml:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http"
android:host="www.example.com" />
<data android:scheme="http"
android:host="www.example2.com" />
</intent-filter>
</activity>
This will launch your MainActivity when the user clicks any of these links:
• http://www.example.com/
• http://www.example2.com/
• http://www.example.com/map
• http://www.example2.com/map
AndroidManifest.xml:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
https://riptutorial.com/ 684
<data android:path="/" />
<data android:path="/map" />
</intent-filter>
</activity>
This will launch your MainActivity when the user clicks any of these links:
• http://www.example.com/
• https://www.example.com/
• http://www.example.com/map
• https://www.example.com/map
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (data != null) {
String param1 = data.getQueryParameter("param1");
String param2 = data.getQueryParameter("param2");
}
}
Using pathPrefix
AndroidManifest.xml:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http"
android:host="www.example.com"
android:path="/item" />
</intent-filter>
</activity>
https://riptutorial.com/ 685
This will launch your MainActivity when the user clicks any link starting with
http://www.example.com/item, such as:
• https://www.example.com/item
• http://www.example.com/item/1234
• https://www.example.com/item/xyz/details
https://riptutorial.com/ 686
Chapter 127: Handling touch and motion
events
Introduction
A summary of some of the basic touch/motion-handling systems in the Android API.
Parameters
Listener Details
Similar to onTouch, but listens for long presses in buttons, surfaces and
onLongTouch
more.
Examples
Buttons
@Override
public void onCreate(Bundle sis){
super.onCreate(sis);
setContentView(R.layout.layout);
onLong = (Button) findViewById(R.id.onLong);
onClick = (Button) findViewById(R.id.onClick);
// The buttons are created. Now we need to tell the system that
// these buttons have a listener to check for touch events.
// "this" refers to this class, as it contains the appropriate event listeners.
onLong.setOnLongClickListener(this);
onClick.setOnClickListener(this);
[OR]
onClick.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
// Take action. This listener is only designed for one button.
// This means, no other input will come here.
https://riptutorial.com/ 687
// This makes a switch statement unnecessary here.
}
});
onLong.setOnLongClickListener(new View.OnLongClickListener(){
@Override
public boolean onLongClick(View v){
// See comment in onClick.setOnClickListener().
}
});
}
@Override
public void onClick(View v) {
// If you have several buttons to handle, use a switch to handle them.
switch(v.getId()){
case R.id.onClick:
// Take action.
break;
}
}
@Override
public boolean onLongClick(View v) {
// If you have several buttons to handle, use a switch to handle them.
switch(v.getId()){
case R.id.onLong:
// Take action.
break;
}
return false;
}
}
Surface
Touch event handler for surfaces (e.g. SurfaceView, GLSurfaceView, and others):
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
@Override
public boolean onTouch(View v, MotionEvent event) {
// Add a switch (see buttons example) if you handle multiple views
// here you can see (using MotionEvent event) to see what touch event
// is being taken. Is the pointer touching or lifted? Is it moving?
return false;
https://riptutorial.com/ 688
}
}
// What can you do here? Check if the amount of pointers are [x] and take action,
// if a pointer leaves, a new enters, or the [x] pointers are moved.
// Some examples as to handling etc. touch/motion events.
switch (MotionEventCompat.getActionMasked(e)) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
// One or more pointers touch the screen.
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
// One or more pointers stop touching the screen.
break;
case MotionEvent.ACTION_MOVE:
// One or more pointers move.
if(e.getPointerCount() == 2){
move();
}else if(e.getPointerCount() == 1){
paint();
}else{
zoom();
}
break;
}
return true; // Allow repeated action.
}
}
https://riptutorial.com/ 689
Read Handling touch and motion events online: https://riptutorial.com/android/topic/9315/handling-
touch-and-motion-events
https://riptutorial.com/ 690
Chapter 128: Hardware Button Events/Intents
(PTT, LWP, etc.)
Introduction
Several android devices have custom buttons added by the manufacturer. This opens new
possibilities for the developer in handling those buttons especially when making Apps targeted for
Hardware Devices.
This topic documents buttons which have intents attached to them which you can listen for via
intent-receivers.
Examples
Sonim Devices
PTT_KEY
com.sonim.intent.action.PTT_KEY_DOWN
com.sonim.intent.action.PTT_KEY_UP
YELLOW_KEY
com.sonim.intent.action.YELLOW_KEY_DOWN
com.sonim.intent.action.YELLOW_KEY_UP
SOS_KEY
com.sonim.intent.action.SOS_KEY_DOWN
com.sonim.intent.action.SOS_KEY_UP
GREEN_KEY
com.sonim.intent.action.GREEN_KEY_DOWN
com.sonim.intent.action.GREEN_KEY_UP
https://riptutorial.com/ 691
Registering the buttons
To receive those intents you will have to assign the buttons to your app in the Phone-Settings.
Sonim has a possibilty to auto-register the buttons to the App when it is installed. In order to do
that you will have to contact them and get a package-specific key to include in your Manifest like
this:
<meta-data
android:name="app_key_green_data"
android:value="your-key-here" />
RugGear Devices
PTT Button
android.intent.action.PTT.down
android.intent.action.PTT.up
https://riptutorial.com/ 692
Chapter 129: How to store passwords
securely
Examples
Using AES for salted password encryption
This examples uses the AES algorithm for encrypting passwords. The salt length can be up to 128
bit.
We are using the SecureRandom class to generate a salt, which is combined with the password to
generate a secret key. The classes used are already existing in Android packages javax.crypto
and java.security.
Once a key is generated, we have to preserve this key in a variable or store it. We are storing it
among the shared preferences in the value S_KEY. Then, a password is encrypted using the doFinal
method of the Cipher class once it is initialised in ENCRYPT_MODE. Next, the encrypted password is
converted from a byte array into a string and stored among the shared preferences. The key used
to generate an encrypted password can be used to decrypt the password in a similar way:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String originalPassword = "ThisIsAndroidStudio%$";
Log.e(TAG, "originalPassword => " + originalPassword);
String encryptedPassword = encryptAndStorePassword(originalPassword);
Log.e(TAG, "encryptedPassword => " + encryptedPassword);
String decryptedPassword = decryptAndGetPassword();
Log.e(TAG, "decryptedPassword => " + decryptedPassword);
}
https://riptutorial.com/ 693
SecretKey aesKey = new SecretKeySpec(encoded, SECRET_KEY_ALGORITHM);
passwrd = decrypt(aesKey, encryptedPasswrd);
} catch (Exception e) {
e.printStackTrace();
}
}
return passwrd;
}
} catch (Exception e) {
Log.e("SecurityException", e.getCause().getLocalizedMessage());
throw new Exception("Unable to encrypt", e);
}
}
https://riptutorial.com/ 694
Log.e("SecurityException", e.getCause().getLocalizedMessage());
throw new Exception("Unable to decrypt", e);
}
}
https://riptutorial.com/ 695
Read How to store passwords securely online: https://riptutorial.com/android/topic/9093/how-to-
store-passwords-securely
https://riptutorial.com/ 696
Chapter 130: How to use SparseArray
Introduction
A SparseArray is an alternative for a Map. A Map requires its keys to be objects. The phenomenon of
autoboxing occurs when we want to use a primitive int value as key. The compiler automatically
converts primitive values to their boxed types (e.g. int to Integer). The difference in memory
footprint is noticeable: int uses 4 bytes, Integer uses 16 bytes. A SparseArray uses int as key
value.
Remarks
Advantage :
Disadvantage :
• SparseArray uses binary search for find value ( O(log n) ), so its may not be the best solution
if have to work with large number of element(use HashMap).
There are several variants of the family like : -SparseArray <Integer, Object> -
SparseBooleanArray <Integer, Boolean> -SparseIntArray <Integer, Integer> -SparseLongArray
<Integer, Long> -LongSparseArray <Long, Object> -LongSparseLongArray <Long, Long>
SparseArray operations
• adding element -- put(int, x): Adds a mapping from the specified key to the specified value,
replacing the previous mapping from the specified key if there was one. -- append(int, x):
Puts a key/value pair into the array, optimizing for the case where the key is greater than all
existing keys in the array. You should use append() in case of sequential keys to optimize
performance. Otherwise put() is fine.
• removing element -- delete(int): Removes the mapping from the specified key, if there was
any. -- removeAt(int): Removes the mapping at the given index. -- removeAtRange(int, int):
Remove a range of mappings as a batch.
• accessing element -- get(int): Gets the int mapped from the specified key, or 0 if no such
mapping has been made. -- get(int, E): Gets the int mapped from the specified key, or the
specified value if no such mapping has been made. -- valueAt(int): Given an index in the
range 0...size()-1, returns the value from the indexth key-value mapping that this
SparseIntArray stores. Indices are ordered in ascending order.
• index/key search -- keyAt(int): Given an index in the range 0...size()-1, returns the key from
the indexth key-value mapping that this SparseIntArray stores. Indices are ordered in
https://riptutorial.com/ 697
ascending order. -- valueAt(int): Given an index in the range 0...size()-1, returns the value
from the indexth key-value mapping that this SparseIntArray stores. Indices are ordered in
ascending order. -- indexOfKey(int): Returns the index for which keyAt(int) would return the
specified key, or a negative number if the specified key is not mapped. -- indexOfValue(E):
Returns an index for which valueAt(int) would return the specified key, or a negative number
if no keys map to the specified value. Beware that this is a linear search, unlike lookups by
key, and that multiple keys can map to the same value and this will find only one of them.
The difference in their memory footprint is noticeable: the int uses 4 bytes, the Integer uses
16 bytes.SparseArray uses int as key value.
Examples
Basic example using SparseArray
class Person {
String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@Override
public int hashCode() {
return name != null ? name.hashCode() : 0;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
https://riptutorial.com/ 698
// Find the person with identifier 1234.
Person id1234 = demo.get(1234); // Returns John.
Tutorial on YouTube
https://riptutorial.com/ 699
Chapter 131: HttpURLConnection
Syntax
• abstract void disconnect()
• abstract boolean usingProxy()
• static boolean getFollowRedirects()
• static void setFollowRedirects(boolean set)
• String getHeaderField(int n)
• String getHeaderFieldKey(int n)
• String getRequestMethod()
• String getResponseMessage()
• int getResponseCode()
• long getHeaderFieldDate(String name, long Default)
• boolean getInstanceFollowRedirects()
• Permission getPermission()
• InputStream getErrorStream()
• void setChunkedStreamingMode(int chunklen)
• void setFixedLengthStreamingMode(int contentLength)
• void setFixedLengthStreamingMode(long contentLength)
• void setInstanceFollowRedirects(boolean followRedirects)
• void setRequestMethod(String method)
Remarks
HttpURLConnection is the standard HTTP client for Android, used to send and receive data over the
web. It is a concrete implementation of URLConnection for HTTP (RFC 2616).
Examples
Creating an HttpURLConnection
In order to create a new Android HTTP Client HttpURLConnection, call openConnection() on a URL
instance. Since openConnection() returns a URLConnection, you need to explicitly cast the returned
value.
If you are creating a new URL, you also have to handle the exceptions associated with URL
parsing.
try {
https://riptutorial.com/ 700
URL url = new URL("http://example.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// do something with the connection
} catch (MalformedURLException e) {
e.printStackTrace();
}
Once the response body has been read and the connection is no longer required, the connection
should be closed by calling disconnect().
Here is an example:
try {
BufferedReader br = new BufferedReader(new
InputStreamReader(connection.getInputStream()));
} finally {
connection.disconnect();
}
Please note that exceptions are not handled in the example above. A full example, including (a
trivial) exception handling, would be:
URL url;
HttpURLConnection connection = null;
try {
url = new URL("http://example.com");
connection = (HttpURLConnection) url.openConnection();
BufferedReader br = new BufferedReader(new
InputStreamReader(connection.getInputStream()));
} catch (IOException e) {
https://riptutorial.com/ 701
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
}
try {
BufferedReader br = new BufferedReader(new
InputStreamReader(connection.getInputStream()));
Log.d("HTTP-GET", body);
} finally {
connection.disconnect();
}
Please note that exceptions are not handled in the example above.
MultipartUtility.java
/**
* This constructor initializes a new HTTP POST request with content type
* is set to multipart/form-data
*
* @param requestURL
* @param charset
https://riptutorial.com/ 702
* @throws IOException
*/
public MultipartUtility(String requestURL, String charset)
throws IOException {
this.charset = charset;
/**
* Adds a form field to the request
*
* @param name field name
* @param value field value
*/
public void addFormField(String name, String value) {
writer.append("--" + boundary).append(LINE_FEED);
writer.append("Content-Disposition: form-data; name=\"" + name + "\"")
.append(LINE_FEED);
writer.append("Content-Type: text/plain; charset=" + charset).append(
LINE_FEED);
writer.append(LINE_FEED);
writer.append(value).append(LINE_FEED);
writer.flush();
}
/**
* Adds a upload file section to the request
*
* @param fieldName name attribute in <input type="file" name="..." />
* @param uploadFile a File to be uploaded
* @throws IOException
*/
public void addFilePart(String fieldName, File uploadFile)
throws IOException {
String fileName = uploadFile.getName();
writer.append("--" + boundary).append(LINE_FEED);
writer.append(
"Content-Disposition: form-data; name=\"" + fieldName
+ "\"; filename=\"" + fileName + "\"")
.append(LINE_FEED);
writer.append(
"Content-Type: "
+ URLConnection.guessContentTypeFromName(fileName))
.append(LINE_FEED);
writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
writer.append(LINE_FEED);
writer.flush();
https://riptutorial.com/ 703
byte[] buffer = new byte[4096];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
inputStream.close();
writer.append(LINE_FEED);
writer.flush();
}
/**
* Adds a header field to the request.
*
* @param name - name of the header field
* @param value - value of the header field
*/
public void addHeaderField(String name, String value) {
writer.append(name + ": " + value).append(LINE_FEED);
writer.flush();
}
/**
* Completes the request and receives response from the server.
*
* @return a list of Strings as response in case the server returned
* status OK, otherwise an exception is thrown.
* @throws IOException
*/
public List<String> finish() throws IOException {
List<String> response = new ArrayList<String>();
writer.append(LINE_FEED).flush();
writer.append("--" + boundary + "--").append(LINE_FEED);
writer.close();
// In your case you are not adding form data so ignore this
/*This is to add parameter values */
for (int i = 0; i < myFormDataArray.size(); i++) {
https://riptutorial.com/ 704
multipart.addFormField(myFormDataArray.get(i).getParamName(),
myFormDataArray.get(i).getParamValue());
}
Use a HashMap to store the parameters that should be sent to the server through POST
parameters:
Once the params HashMap is populated, create the StringBuilder that will be used to send them to
the server:
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
i++;
}
Then, create the HttpURLConnection, open the connection, and send the POST parameters:
try{
String url = "http://www.example.com/test.php";
URL urlObj = new URL(url);
HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Accept-Charset", "UTF-8");
https://riptutorial.com/ 705
conn.setReadTimeout(10000);
conn.setConnectTimeout(15000);
conn.connect();
try {
InputStream in = new BufferedInputStream(conn.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (conn != null) {
conn.disconnect();
}
}
Quite often it's necessary to send/upload a file to a remote server, for example, an image, video,
audio or a backup of the application database to a remote private server. Assuming the server is
expecting a POST request with the content, here's a simple example of how to complete this task
in Android.
File uploads are sent using multipart/form-data POST requests. It's very easy to implement:
https://riptutorial.com/ 706
DataOutputStream request = new DataOutputStream(uc.getOutputStream());
switch(respCode) {
case 200:
//all went ok - read response
...
break;
case 301:
case 302:
case 307:
//handle redirect - for example, re-post to the new location
...
break;
...
default:
//do something sensible
}
Of course, exceptions will need to be caught or declared as being thrown. A couple points to note
about this code:
1. postTarget is the destination URL of the POST; oauthToken is the authentication token;
fileDescription is the description of the file, which is sent as the value of field description;
file is the file to be sent - it's of type java.io.File - if you have the file path, you can use new
File(filePath) instead.
2. It sets Authorization header for an oAuth auth
3. It uses Apache Common FileUtil to read the file into a byte array - if you already have the
content of the file in a byte array or in some other way in memory, then there's no need to
read it.
The following class can be used as a single class that can handle GET, POST, PUT, PATCH, and other
requests:
class APIResponseObject{
int responseCode;
String response;
https://riptutorial.com/ 707
{
this.responseCode = responseCode;
this.response = response;
}
}
interface OnCompleteListener{
void onComplete(APIResponseObject result);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected APIResponseObject doInBackground(String... params) {
Log.d("debug", "url = "+ requestUrl);
try {
urlConnection = (HttpURLConnection) requestUrl.openConnection();
if(headerData != null) {
for (Pair pair : headerData) {
urlConnection.setRequestProperty(pair.first.toString(),pair.second.toString());
https://riptutorial.com/ 708
}
}
urlConnection.setDoInput(true);
urlConnection.setChunkedStreamingMode(0);
urlConnection.setRequestMethod(method);
urlConnection.connect();
if(!(method.equals("GET"))) {
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-
8"));
writer.write(getPostDataString(postData));
writer.flush();
writer.close();
out.close();
}
urlConnection.connect();
responseCode = urlConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-
8"));
String line;
@Override
protected void onPostExecute(APIResponseObject result) {
delegate.onComplete(result);
super.onPostExecute(result);
}
result.append(URLEncoder.encode(pair.first,"UTF-8"));
result.append("=");
result.append(URLEncoder.encode(pair.second, "UTF-8"));
}
https://riptutorial.com/ 709
return result.toString();
}
}
Usage
Use any of the given constructors of the class depending on whether you need to send POST data
or any extra headers.
The onComplete() method will be called when the data fetching is complete. The data is returned as
an object of the APIResponseObject class, which has a status code stating the HTTP status code of
the request and a string containing the response. You can parse this response in your class, i.e.
XML or JSON.
Call execute() on the object of the class to execute the request, as shown in the following example:
class MainClass {
String url = "https://example.com./api/v1/ex";
String method = "POST";
List<Pair<String,String>> postData = new ArrayList<>();
postData.add(new Pair<>("email","whatever");
postData.add(new Pair<>("password", "whatever");
https://riptutorial.com/ 710
Chapter 132: Image Compression
Examples
How to compress image without size change
ImageUtils.java:
}
}
https://riptutorial.com/ 711
options.inJustDecodeBounds = false;
options.inDither = false;
options.inPurgeable = true;
options.inInputShareable = true;
options.inTempStorage = new byte[16 * 1024];
try {
bmp = BitmapFactory.decodeFile(imagePath, options);
} catch (OutOfMemoryError exception) {
exception.printStackTrace();
}
try {
scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight,
Bitmap.Config.ARGB_8888);
} catch (OutOfMemoryError exception) {
exception.printStackTrace();
}
return updatedBitmap;
}
https://riptutorial.com/ 712
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
Output:
Before Compress : Dimension: 1080-1452
After Compress : Dimension: 1080-1452
https://riptutorial.com/ 713
Chapter 133: ImageView
Introduction
ImageView (android.widget.ImageView) is a View for displaying and manipulating image resources,
such as Drawables and Bitmaps.
Some effects, discussed in this topic, can be applied to the image. The image source can be set in
XML file (layout folder) or by programatically in Java code.
Syntax
• The method setImageResource(int resId) sets a drawable as the content of this ImageView.
• Usage: imageView.setImageResource(R.drawable.anyImage)
Parameters
Parameter Description
resId your Image file name in the res folder (usually in drawable folder)
Examples
Set Image Resource
<ImageView
android:id="@+id/imgExample"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
...
/>
android:src="@drawable/android2"
Set alpha
https://riptutorial.com/ 714
set alpha using XML attribute:
android:alpha="0.5"
imgExample.setAlpha(0.5f);
The image contained in the ImageView may not fit the exact size given to the container. In that
case, the framework allows you to resize the image in a number of ways.
https://riptutorial.com/ 715
Center
<ImageView android:layout_width="20dp"
android:layout_height="20dp"
android:src="@mipmap/ic_launcher"
android:id="@+id/imageView"
android:scaleType="center"
android:background="@android:color/holo_orange_light"/>
This will not resize the image, and it will center it inside the container (Orange = container)
https://riptutorial.com/ 716
https://riptutorial.com/ 717
which has a black background and we want to display a rectangular drawable in white background
in ImageView.
<ImageView
android:id="@+id/imgExample"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#000"
android:src="@drawable/android2"
android:scaleType="..."/>
2. centerCrop: Scale the image uniformly (maintain the image's aspect ratio) so both dimensions
(width and height) of the image will be equal to or larger than the corresponding dimension of
the view (minus padding). The image is then centered in the view.
https://riptutorial.com/ 718
3. centerInside: Scale the image uniformly (maintain the image's aspect ratio) so that both
dimensions (width and height) of the image will be equal to or less than the corresponding
dimension of the view (minus padding). The image is then centered in the view.
https://riptutorial.com/ 719
5. fitXY: Scale the image using FILL.
https://riptutorial.com/ 720
7. fitCenter: Scale the image using CENTER.
https://riptutorial.com/ 721
Set tint
Set a tinting color for the image. By default, the tint will blend using SRC_ATOP mode.
android:tint="#009c38"
Note: Must be a color value, in the form of "#rgb", "#argb", "#rrggbb", or "#aarrggbb".
imgExample.clearColorFilter();
Example:
https://riptutorial.com/ 722
MLRoundedImageView.java
@Override
protected void onDraw(Canvas canvas) {
if (drawable == null) {
return;
}
if (getWidth() == 0 || getHeight() == 0) {
return;
}
Bitmap b = ((BitmapDrawable) drawable).getBitmap();
Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);
https://riptutorial.com/ 723
canvas.drawBitmap(roundBitmap, 0, 0, null);
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(Color.parseColor("#BAB399"));
canvas.drawCircle(radius / 2 + 0.7f,
radius / 2 + 0.7f, radius / 2 + 0.1f, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(sbmp, rect, rect, paint);
return output;
}
}
<com.androidbuts.example.MLRoundedImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher" />
https://riptutorial.com/ 724
Chapter 134: Implicit Intents
Syntax
• Intent()
• Intent (Intent o)
• Intent (String action)
• Intent (String action, Uri uri)
• Intent (Context packageContext, Class<?> cls)
• Intent (String action, Uri uri, Context packageContext, Class<?> cls)
Parameters
Parameters Details
o Intent
cls Class: The component class that is to be used for the intent.
Remarks
• More about Intent
Examples
Implicit and Explicit Intents
An explicit intent is used for starting an activity or service within the same application package. In
this case the name of the intended class is explicitly mentioned:
However, an implicit intent is sent across the system for any application installed on the user's
device that can handle that intent. This is used to share information between different applications.
https://riptutorial.com/ 725
Intent intent = new Intent("com.stackoverflow.example.VIEW");
//We need to check to see if there is an application installed that can handle this intent
if (getPackageManager().resolveActivity(intent, 0) != null){
startActivity(intent);
}else{
//Handle error
}
More details on the differences can be found in the Android Developer docs here: Intent
Resolution
Implicit Intents
Implicit intents do not name a specific component, but instead declare a general action to perform,
which allows a component from another app to handle it.
For example, if you want to show the user a location on a map, you can use an implicit intent to
request that another capable app show a specified location on a map.
Example:
https://riptutorial.com/ 726
Chapter 135: In-app Billing
Examples
Consumable In-app Purchases
Consumable Managed Products are products that can be bought multiple times such as in-game
currency, game lives, power-ups, etc.
In this example, we are going to implement 4 different consumable managed products "item1",
"item2", "item3", "item4".
Steps in summary:
1. Add the In-app Billing library to your project (AIDL File).
2. Add the required permission in AndroidManifest.xml file.
3. Deploy a signed apk to Google Developers Console.
4. Define your products.
5. Implement the code.
6. Test In-app Billing (optional).
Step 1:
First of all, we will need to add the AIDL file to your project as clearly explained in Google
Documentation here.
IInAppBillingService.aidl is an Android Interface Definition Language (AIDL) file that defines the
interface to the In-app Billing Version 3 service. You will use this interface to make billing requests
by invoking IPC method calls.
Step 2:
After adding the AIDL file, add BILLING permission in AndroidManifest.xml:
Step 3:
Generate a signed apk, and upload it to Google Developers Console. This is required so that we
can start defining our in-app products there.
https://riptutorial.com/ 727
Step 4:
Define all your products with different productID, and set a price to each one of them. There are 2
types of products (Managed Products and Subscriptions). As we already said, we are going to
implement 4 different consumable managed products "item1", "item2", "item3", "item4".
Step 5:
After doing all the steps above, you are now ready to start implementing the code itself in your
own activity.
MainActivity:
IInAppBillingService inAppBillingService;
ServiceConnection serviceConnection;
// productID for each item. You should define them in the Google Developers Console.
final String item1 = "item1";
final String item2 = "item2";
final String item3 = "item3";
final String item4 = "item4";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buy2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
buyItem(item2);
}
});
buy3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
https://riptutorial.com/ 728
buyItem(item3);
}
});
buy4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
buyItem(item4);
}
});
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
inAppBillingService = IInAppBillingService.Stub.asInterface(service);
}
};
// Get the price of each product, and set the price as text to
// each button so that the user knows the price of each item.
if (inAppBillingService != null) {
// Attention: You need to create a new thread here because
// getSkuDetails() triggers a network request, which can
// cause lag to your app if it was called from the main thread.
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
ArrayList<String> skuList = new ArrayList<>();
skuList.add(item1);
skuList.add(item2);
skuList.add(item3);
skuList.add(item4);
Bundle querySkus = new Bundle();
querySkus.putStringArrayList("ITEM_ID_LIST", skuList);
try {
Bundle skuDetails = inAppBillingService.getSkuDetails(3,
getPackageName(), "inapp", querySkus);
int response = skuDetails.getInt("RESPONSE_CODE");
if (response == 0) {
ArrayList<String> responseList =
skuDetails.getStringArrayList("DETAILS_LIST");
https://riptutorial.com/ 729
switch (sku) {
case item1:
buy1.setText(price);
break;
case item2:
buy2.setText(price);
break;
case item3:
buy3.setText(price);
break;
case item4:
buy4.setText(price);
break;
}
}
}
} catch (RemoteException | JSONException e) {
e.printStackTrace();
}
}
});
thread.start();
}
}
// Launch the PurchaseFlow passing the productID of the item the user wants to buy as a
parameter.
private void buyItem(String productID) {
if (inAppBillingService != null) {
try {
Bundle buyIntentBundle = inAppBillingService.getBuyIntent(3, getPackageName(),
productID, "inapp", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");
PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
startIntentSenderForResult(pendingIntent.getIntentSender(), 1003, new
Intent(), 0, 0, 0);
} catch (RemoteException | IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
}
// Check here if the in-app purchase was successful or not. If it was successful,
// then consume the product, and let the app make the required changes.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
https://riptutorial.com/ 730
// Attention: You need to create a new thread here because
// consumePurchase() triggers a network request, which can
// cause lag to your app if it was called from the main thread.
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
JSONObject jo = new JSONObject(purchaseData);
// Get the productID of the purchased item.
String sku = jo.getString("productId");
String productName = null;
// Consume the purchase so that the user is able to purchase the same
product again.
inAppBillingService.consumePurchase(3, getPackageName(),
jo.getString("purchaseToken"));
Toast.makeText(MainActivity.this, productName + " is successfully
purchased. Excellent choice, master!", Toast.LENGTH_LONG).show();
} catch (JSONException | RemoteException e) {
Toast.makeText(MainActivity.this, "Failed to parse purchase data.",
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
});
thread.start();
}
}
}
Step 6:
After implementing the code, you can test it by deploying your apk to beta/alpha channel, and let
other users test the code for you. However, real in-app purchases can't be made while in testing
mode. You have to publish your app/game first to Play Store so that all the products are fully
https://riptutorial.com/ 731
activated.
Step 1: First of all follow these two steps to add in app functionality :
repositories {
mavenCentral()
}
dependencies {
compile 'com.anjlab.android.iab.v3:library:1.0.+'
}
BillingProcessor bp = new BillingProcessor(this, "YOUR LICENSE KEY FROM GOOGLE PLAY CONSOLE
HERE", this);
https://riptutorial.com/ 732
Read In-app Billing online: https://riptutorial.com/android/topic/2843/in-app-billing
https://riptutorial.com/ 733
Chapter 136: Installing apps with ADB
Examples
Install an app
Note that you have to pass a file that is on your computer and not on your device.
If you append -r at the end, then any existing conflicting apks will be overwritten. Otherwise, the
command will quit with an error.
Uninstall an app
Write the following command in your terminal to uninstall an app with a provided package name:
Windows :
Linux :
https://riptutorial.com/ 734
Chapter 137: Instant Run in Android Studio
Remarks
Instant Run is an extended behavior for the run and debug commands that enables faster
debugging by not requiring a full build and reinstall for eevry change done in your app's code.
Introduced in Android Studio 2.0, Instant Run is a behavior for the Run and Debug
commands that significantly reduces the time between updates to your app. Although
your first build may take longer to complete, Instant Run pushes subsequent updates
to your app without building a new APK, so changes are visible much more quickly.
Instant Run is supported only when you deploy the debug build variant, use Android
Plugin for Gradle version 2.0.0 or higher, and set minSdkVersion to 15 or higher in your
app's module-level build.gradle file. For the best performance, set minSdkVersion to 21
or higher.
After deploying an app, a small, yellow thunderbolt icon appears within the Run button
(or Debug button), indicating that Instant Run is ready to push updates the next time
you click the button. Instead of building a new APK, it pushes just those new changes
and, in some cases, the app doesn't even need to restart but immediately shows the
effect of those code changes.
Instant Run pushes updated code and resources to your connected device or emulator
by performing a hot swap, warm swap, or cold swap. It automatically determines the
type of swap to perform based on the type of change you made. The video above
provides interesting detail about how this all works under the hood. For a quick
summary of how Instant Run behaves when you push certain code changes to a target
device, however, see the following table.
Documentation
Examples
Enabling or disabling Instant Run
https://riptutorial.com/ 735
The top option is Instant run. Check/uncheck that box.
Documentation
https://riptutorial.com/ 736
Types of code Swaps in Instant Run
There are three types of code swaps that Instant run enables to support faster debugging and
running app from your code in Android Studio.
• Hot Swap
• Warm Swap
• Cold Swap
WARM SWAP is triggered when an existing resource is changed or removed (anything in the res
folder)
COLD SWAP whenever there is a structural code change in your app's code e.g.
• an annotation
• an instance field
• a static field
• a static method signature
• an instance method signature
HOT SWAP changes are visible instantly - as soon as the next call to the method whose
implementation is changed is made.
There are a few changes where instant won't do its trick and a full build and reinstall fo your app
will happen just like it used to happen before Instant Run was born.
Documentation
https://riptutorial.com/ 737
Read Instant Run in Android Studio online: https://riptutorial.com/android/topic/2119/instant-run-in-
android-studio
https://riptutorial.com/ 738
Chapter 138: Integrate Google Sign In
Syntax
• newInstance() - To create single instance of Google Helper
• initGoogleSignIn() - To initialize Google log in
• getGoogleAccountDetails() - To get logged in Account details
• signOut() - To log out user
• getGoogleClient() - To get GoogleApiClient used
Parameters
Parameter Detail
Examples
Google Sign In with Helper class
/**
* Created by Andy
*/
public class GoogleSignInHelper implements GoogleApiClient.OnConnectionFailedListener,
GoogleApiClient.ConnectionCallbacks {
private static final String TAG = GoogleSignInHelper.class.getSimpleName();
https://riptutorial.com/ 739
private GoogleApiClient mGoogleApiClient;
public static final int RC_SIGN_IN = 9001;
private boolean isLoggingOut = false;
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
// An unresolvable error has occurred and Google APIs (including Sign-In) will not
// be available.
Log.d(TAG, "onConnectionFailed:" + connectionResult);
Toast.makeText(mActivity, "Google Play Services error.", Toast.LENGTH_SHORT).show();
}
if (mGoogleApiClient.isConnected()) {
https://riptutorial.com/ 740
} else {
isLoggingOut = true;
}
}
@Override
public void onConnected(@Nullable Bundle bundle) {
Log.w(TAG, "onConnected");
if (isLoggingOut) {
signOut();
}
}
@Override
public void onConnectionSuspended(int i) {
Log.w(TAG, "onConnectionSuspended");
}
}
// [START onactivityresult]
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// [START signin]
public void signIn() {
Intent signInIntent =
Auth.GoogleSignInApi.getSignInIntent(googleSignInHelper.getGoogleClient());
startActivityForResult(signInIntent, GoogleSignInHelper.RC_SIGN_IN);
}
// [END signin]
https://riptutorial.com/ 741
Chapter 139: Integrate OpenCV into Android
Studio
Remarks
The Open CV libraries can be found on the web by using a search engine.
The Gotchas:
• If you lower your target platform below KitKat some of the OpenCV libraries will no longer
function, specifically the classes related to org.opencv.android.Camera2Renderer and other
related classes. You can probably get around this by simply removing the apprpriate
OpenCV .java files.
• If you raise your target platform to Lollipop or above my example of loading a file might not
work because use of absolute file paths is frowned upon. So you might have to change the
example to load a file from the gallery or somewhere else. There are numerous examples
floating around.
Examples
Instructions
Tested with A.S. v1.4.1 but should work with newer versions too.
1. Create a new Android Studio project using the project wizard (Menu:/File/New Project):
• Call it "cvtest1"
• Form factor: API 19, Android 4.4 (KitKat)
• Blank Activity named MainActivity
You should have a cvtest1 directory where this project is stored. (the title bar of Android
studio shows you where cvtest1 is when you open the project)
2. Verify that your app runs correctly. Try changing something like the "Hello World" text to
confirm that the build/test cycle is OK for you. (I'm testing with an emulator of an API 19
device).
3. Download the OpenCV package for Android v3.1.0 and unzip it in some temporary directory
somewhere. (Make sure it is the package specifically for Android and not just the OpenCV
for Java package.) I'll call this directory "unzip-dir" Below unzip-dir you should have a
sdk/native/libs directory with subdirectories that start with things like arm..., mips... and x86
... (one for each type of "architecture" Android runs on)
https://riptutorial.com/ 742
• Source-directory: {unzip-dir}/sdk/java
• Module name: Android studio automatically fills in this field with openCVLibrary310
(the exact name probably doesn't matter but we'll go with this).
• Click on next. You get a screen with three checkboxes and questions about jars,
libraries and import options. All three should be checked. Click on Finish.
Android Studio starts to import the module and you are shown an import-summary.txt file
that has a list of what was not imported (mostly javadoc files) and other pieces of
information.
https://riptutorial.com/ 743
But you also get an error message saying failed to find target with hash string 'android-
14'.... This happens because the build.gradle file in the OpenCV zip file you downloaded
says to compile using android API version 14, which by default you don't have with Android
Studio v1.4.1.
https://riptutorial.com/ 744
5. Open the project structure dialogue (Menu:/File/Project_Structure). Select the "app"
module, click on the Dependencies tab and add :openCVLibrary310 as a Module
Dependency. When you select Add/Module_Dependency it should appear in the list of
modules you can add. It will now show up as a dependency but you will get a few more
cannot-find-android-14 errors in the event log.
6. Look in the build.gradle file for your app module. There are multiple build.gradle files in an
Android project. The one you want is in the cvtest1/app directory and from the project view it
looks like build.gradle (Module: app). Note the values of these four fields:
7. Your project now has a cvtest1/OpenCVLibrary310 directory but it is not visible from the
project view:
Use some other tool, such as any file manager, and go to this directory. You can also switch the
project view from Android to Project Files and you can find this directory as shown in this
screenshot:
https://riptutorial.com/ 745
Inside there is another build.gradle file (it's highlighted in the above screenshot). Update this file
with the four values from step 6.
8. Resynch your project and then clean/rebuild it. (Menu:/Build/Clean_Project) It should clean
and build without errors and you should see many references to :openCVLibrary310 in the
0:Messages screen.
At this point the module should appear in the project hierarchy as openCVLibrary310, just
like app. (Note that in that little drop-down menu I switched back from Project View to
Android View ). You should also see an additional build.gradle file under "Gradle Scripts"
but I find the Android Studio interface a little bit glitchy and sometimes it does not do this
right away. So try resynching, cleaning, even restarting Android Studio.
You should see the openCVLibrary310 module with all the OpenCV functions under java like
in this screenshot:
https://riptutorial.com/ 746
9. Copy the {unzip-dir}/sdk/native/libs directory (and everything under it) to your Android
project, to cvtest1/OpenCVLibrary310/src/main/, and then rename your copy from libs to
jniLibs. You should now have a cvtest1/OpenCVLibrary310/src/main/jniLibs directory.
Resynch your project and this directory should now appear in the project view under
openCVLibrary310.
https://riptutorial.com/ 747
10. Go to the onCreate method of MainActivity.java and append this code:
if (!OpenCVLoader.initDebug()) {
Log.e(this.getClass().getSimpleName(), " OpenCVLoader.initDebug(), not working.");
} else {
Log.d(this.getClass().getSimpleName(), " OpenCVLoader.initDebug(), working.");
}
Then run your application. You should see lines like this in the Android Monitor:
https://riptutorial.com/ 748
(I don't know why that line with the error message is there)
11. Now try to actually use some openCV code. In the example below I copied a .jpg file to the
cache directory of the cvtest1 application on the android emulator. The code below loads this
image, runs the canny edge detection algorithm and then writes the results back to a .png
file in the same directory.
Put this code just below the code from the previous step and alter it to match your own
files/directories.
String inputFileName="simm_01";
String inputExtension = "jpg";
String inputDir = getCacheDir().getAbsolutePath(); // use the cache directory for i/o
String outputDir = getCacheDir().getAbsolutePath();
String outputExtension = "png";
String inputFilePath = inputDir + File.separator + inputFileName + "." + inputExtension;
// for the canny edge detection algorithm, play with these to see different results
int threshold1 = 70;
int threshold2 = 100;
https://riptutorial.com/ 749
Mat im_canny = new Mat(); // you have to initialize output image before giving it to the
Canny method
Imgproc.Canny(image, im_canny, threshold1, threshold2);
String cannyFilename = outputDir + File.separator + inputFileName + "_canny-" + threshold1
+ "-" + threshold2 + "." + outputExtension;
Log.d (this.getClass().getSimpleName(), "Writing " + cannyFilename);
Imgcodecs.imwrite(cannyFilename, im_canny);
12. Run your application. Your emulator should create a black and white "edge" image. You can
use the Android Device Monitor to retrieve the output or write an activity to show it.
https://riptutorial.com/ 750
Chapter 140: Intent
Introduction
An Intent is a small message passed around the Android system. This message may hold
information about our intention to perform a task.
Syntax
• Intent Intent()
• Intent Intent(Intent intent)
• Intent Intent(String action)
• Intent Intent(String action, Uri uri)
• Intent Intent(Context packageContext, Class<?> cls)
• Intent Intent(String action, Uri uri, Context packageContext, Class<?> cls)
• void startActivity(Intent intent)
• void startActivity(Intent intent, Bundle options)
• void startActivityForResult (Intent intent, int requestCode)
• void startActivityForResult (Intent intent, int requestCode, Bundle options)
• Intent putExtra(String name, double[] value)
• Intent putExtra(String name, int value)
• Intent putExtra(String name, CharSequence value)
• Intent putExtra(String name, char value)
• Intent putExtra(String name, Bundle value)
• Intent putExtra(String name, Parcelable[] value)
• Intent putExtra(String name, Serializable value)
• Intent putExtra(String name, int[] value)
• Intent putExtra(String name, float value)
• Intent putExtra(String name, byte[] value)
• Intent putExtra(String name, long[] value)
• Intent putExtra(String name, Parcelable value)
• Intent putExtra(String name, float[] value)
• Intent putExtra(String name, long value)
• Intent putExtra(String name, String[] value)
• Intent putExtra(String name, boolean value)
• Intent putExtra(String name, boolean[] value)
• Intent putExtra(String name, short value)
• Intent putExtra(String name, double value)
• Intent putExtra(String name, short[] value)
• Intent putExtra(String name, String value)
• Intent putExtra(String name, byte value)
• Intent putExtra(String name, char[] value)
https://riptutorial.com/ 751
• Intent putExtra(String name, CharSequence[] value)
Parameters
Parameter Details
Remarks
PackageManager pm = getActivity().getPackageManager();
if (intent.resolveActivity(pm) != null) {
//intent can be handled
startActivity(intent);
} else {
//intent can not be handled
}
https://riptutorial.com/ 752
Starting Activity which is a singleTask or singleTop
When the activity's launch mode is singleTask or singleTop, the onActivityResult will be called as
soon as the activity is started with a data null. To prevent this, use Intent.setFlags(0) to reset the
default flags.
Examples
Start an activity
1. A Context as its first parameter (this is used because the Activity class is a subclass of
Context)
2. The Class of the app component to which the system should deliver the Intent (in this case,
the activity that should be started)
startActivity(intent);
finish(); // Optionally, you can close OriginActivity. In this way when the user press
back from DestinationActivity he/she won't land on OriginActivity again.
}
}
Another way to create the Intent to open DestinationActivity is to use the default constructor for
the Intent, and use the setClass() method to tell it which Activity to open:
This example illustrates sending a String with value as "Some data!" from OriginActivity to
DestinationActivity.
NOTE: This is the most straightforward way of sending data between two activities. See the
example on using the starter pattern for a more robust implementation.
https://riptutorial.com/ 753
OriginActivity
public class OriginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_origin);
// Add data in the form of key/value pairs to the intent object by using putExtra()
intent.putExtra(DestinationActivity.EXTRA_DATA, "Some data!");
DestinationActivity
public class DestinationActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_destination);
// getIntent() returns the Intent object which was used to start this Activity
final Intent intent = getIntent();
// Retrieve the data from the intent object by using the same key that
// was previously used to add data to the intent object in OriginActivity.
final String data = intent.getStringExtra(EXTRA_DATA);
}
}
It is also possible to pass other primitive data types as well as arrays, Bundle and Parcelable data.
Passing Serializable is also possible, but should be avoided as it is more than three times slower
than Parcelable.
Parcelable is an Android specific interface which can be implemented on custom data types (i.e.
your own objects / POJO objects ), it allows your object to be flattened and reconstruct itself
without the destination needing to do anything. There is a documentation example of making an
object parcelable.
https://riptutorial.com/ 754
Once you have a parcelable object you can send it like a primitive type, with an intent object:
intent.putExtra(DestinationActivity.EXTRA_DATA, myParcelableObject);
bundle.putParcelable(DestinationActivity.EXTRA_DATA, myParcelableObject);
and then also read it from the intent at the destination using getParcelableExtra:
Once you have a Serializable object you can put it in an intent object:
bundle.putSerializable(DestinationActivity.EXTRA_DATA, mySerializableObject);
and then also read it from the intent object at the destination as shown below:
Sending emails
If you need to add an attachment, you can use Intent.ACTION_SEND instead of Intent.ACTION_SENDTO.
For multiple attachments you can use ACTION_SEND_MULTIPLE
A word of caution: not every device has a provider for ACTION_SENDTO, and calling startActivity()
https://riptutorial.com/ 755
without checking with resolveActivity() first may throw an ActivityNotFoundException.
By using startActivityForResult(Intent intent, int requestCode) you can start another Activity
and then receive a result from that Activity in the onActivityResult(int requestCode, int
resultCode, Intent data) method. The result will be returned as an Intent. An intent can contain
data via a Bundle
In this example MainActivity will start a DetailActivity and then expect a result from it. Each
request type should have its own int request code, so that in the overridden onActivityResult(int
requestCode, int resultCode, Intent data) method in MainActivity , it can be determined which
request to process by comparing values of requestCode and REQUEST_CODE_EXAMPLE (though in this
example, there is only one).
MainActivity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
https://riptutorial.com/ 756
// Due to some error or flow of control. No data to retrieve.
}
}
}
}
DetailActivity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
@Override
public void onBackPressed() {
// When the user hits the back button set the resultCode
// as Activity.RESULT_CANCELED to indicate a failure
setResult(Activity.RESULT_CANCELED);
super.onBackPressed();
}
}
https://riptutorial.com/ 757
• Make sure your Activity is not using android:launchMode="singleTask", or it will cause the
Activity to run in a separate task and therefore you will not receive a result from it. If your
Activity uses singleTask as launch mode, it will call onActivityResult() immediately with a
result code of Activity.RESULT_CANCELED.
• You can use explicit or implicit intents when you call startActivityForResult(). When starting
one of your own activities to receive a result, you should use an explicit intent to ensure that
you receive the expected result. An explicit intent is always delivered to its target, no matter
what it contains; the filter is not consulted. But an implicit intent is delivered to a component
only if it can pass through one of the component's filters.
In some cases, the URL may start with "www". If that is the case you will get this exception:
https://riptutorial.com/ 758
The URL must always start with "http://" or "https://". Your code should therefore check for it, as
shown in the following code snippet:
Best Practices
Check if there are no apps on the device that can receive the implicit intent. Otherwise, your app
will crash when it calls startActivity(). To first verify that an app exists to receive the intent, call
resolveActivity() on your Intent object. If the result is non-null, there is at least one app that can
handle the intent and it's safe to call startActivity(). If the result is null, you should not use the
intent and, if possible, you should disable the feature that invokes the intent.
Sometimes you may want to start a new activity while removing previous activities from the back
stack, so the back button doesn't take you back to them. One example of this might be starting an
app on the Login activity, taking you through to the Main activity of your application, but on logging
out you want to be directed back to Login without a chance to go back. In a case like that you can
set the FLAG_ACTIVITY_CLEAR_TOP flag for the intent, meaning if the activity being launched is already
running in the current task (LoginActivity), then instead of launching a new instance of that activity,
all of the other activities on top of it will be closed and this Intent will be delivered to the (now on
top) old activity as a new Intent.
It's also possible to use the flags FLAG_ACTIVITY_NEW_TASK along with FLAG_ACTIVITY_CLEAR_TASK if you
want to clear all Activities on the back stack:
Intent URI
<a href="intent://host.com/path#Intent;package=com.sample.test;scheme=yourscheme;end">Start
intent</a>
https://riptutorial.com/ 759
This intent will start app with package com.sample.test or will open google play with this package.
In activity this host and path can be obtained from intent data:
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
Uri data = getIntent().getData(); // returns host.com/path
}
Intents can be used to broadcast messages to other components of your application (such as a
running background service) or to the entire Android system.
To send a broadcast to components outside of your application, use the sendBroadcast() method
on a Context object.
context.sendBroadcast(intent);
4.0.3
https://riptutorial.com/ 760
Using a CustomTabsIntent, it is now possible to configure Chrome custom tabs in order to customize
key UI components in the browser that is opened from your app.
This is a good alternative to using a WebView for some cases. It allows loading of a web page with
an Intent, with the added ability to inject some degree of the look and feel of your app into the
browser.
Note:
To use custom tabs, you need to add this dependency to your build.gradle
compile 'com.android.support:customtabs:24.1.1'
The String List passed as a parameter to the share() method contains the paths of all the files you
want to share.
It basically loops through the paths, adds them to Uri, and starts the Activity which can accept
Files of this type.
https://riptutorial.com/ 761
Starter Pattern
This pattern is a more strict approach to starting an Activity. Its purpose is to improve code
readability, while at the same time decrease code complexity, maintenance costs, and coupling of
your components.
The following example implements the starter pattern, which is usually implemented as a static
method on the Activity itself. This static method accepts all required parameters, constructs a
valid Intent from that data, and then starts the Activity.
An Intent is an object that provides runtime binding between separate components, such as two
activities. The Intent represents an app’s "intent to do something." You can use intents for a wide
variety of tasks, but here, your intent starts another activity.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
This pattern also allows you to force additional data to be passed with the intent.
The ExampleActivity can then be started like this, where context is an activity context:
A Service is a component which runs in the background (on the UI thread) without direct
interaction with the user. An unbound Service is just started, and is not bound to the lifecycle of
any Activity.
https://riptutorial.com/ 762
Intent i= new Intent(context, ServiceName.class);
// potentially add data to the intent extras
i.putExtra("KEY1", "Value to be used by the service");
context.startService(i);
You can use any extras from the intent by using an onStartCommand() override:
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
if (intent != null) {
Bundle extras = intent.getExtras();
String key1 = extras.getString("KEY1", "");
if (key1.equals("Value to be used by the service")) {
//do something
}
}
return START_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
Share intent
This example shows how to open a default dialer (an app that makes regular calls) with a provided
telephone number already in place:
https://riptutorial.com/ 763
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:9988776655")); //Replace with valid phone number. Remember to
add the tel: prefix, otherwise it will crash.
startActivity(intent);
You can pass latitude, longitude from your app to Google map using Intent
SenderActivity
https://riptutorial.com/ 764
myIntent.putExtra("intVariableName", intValue);
startActivity(myIntent);
ReceiverActivity
SenderActivity
ReceiverActivity
SenderActivity
ReceiverActivity
or
SenderActivity
ReceiverActivity
https://riptutorial.com/ 765
Intent mIntent = getIntent();
arrayList = mIntent.getStringArrayListExtra("arrayListVariableName");
SenderActivity
ReceiverActivity
Note : Keep in mind your custom Class must implement the Serializable interface.
SenderActivity
ReceiverActivity
SenderActivity
ReceiverActivity
https://riptutorial.com/ 766
Starting a File Chooser Activity
// Only pick openable and local files. Theoretically we could pull files from google drive
// or other applications that have networked files, but that's unnecessary for this
example.
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
// REQUEST_CODE = <some-integer>
startActivityForResult(intent, REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// If the user doesn't pick a file just return
if (requestCode != REQUEST_CODE || resultCode != RESULT_OK) {
return;
}
// Done!
}
/**
* Obtains the file name for a URI using content resolvers. Taken from the following link
* https://developer.android.com/training/secure-file-sharing/retrieve-
info.html#RetrieveFileInfo
*
* @param uri a uri to query
* @return the file name with no path
* @throws IllegalArgumentException if the query is null, empty, or the column doesn't exist
*/
private String getFileName(Uri uri) throws IllegalArgumentException {
// Obtain a cursor with information regarding this uri
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor.getCount() <= 0) {
https://riptutorial.com/ 767
cursor.close();
throw new IllegalArgumentException("Can't obtain file name, cursor is empty");
}
cursor.moveToFirst();
String fileName =
cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
cursor.close();
return fileName;
}
/**
* Copies a uri reference to a temporary file
*
* @param uri the uri used as the input stream
* @param tempFile the file used as an output stream
* @return the input tempFile for convenience
* @throws IOException if an error occurs
*/
private File copyToTempFile(Uri uri, File tempFile) throws IOException {
// Obtain an input stream from the uri
InputStream inputStream = getContentResolver().openInputStream(uri);
if (inputStream == null) {
throw new IOException("Unable to obtain input stream from URI");
}
return tempFile;
}
It is also possible to pass your custom object to other activities using the Bundle class.
Parcelable
Parcelable processing is much faster than serializable. One of the reasons for this is that we are
being explicit about the serialization process instead of using reflection to infer it. It also stands to
reason that the code has been heavily optimized for this purpose.
https://riptutorial.com/ 768
private ArrayList<String> address;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(age);
dest.writeString(name);
dest.writeStringList(address);
}
@Override
public MyObjects createFromParcel(Parcel source) {
return new MyObjects(source);
}
};
}
https://riptutorial.com/ 769
//Passing MyOject
Intent mIntent = new Intent(FromActivity.this, ToActivity.class);
mIntent.putExtra("UniqueKey", mObject);
startActivity(mIntent);
//Getting MyObjects
Intent mIntent = getIntent();
MyObjects workorder = (MyObjects) mIntent.getParcelable("UniqueKey");
//Array of MyObjects
ArrayList<MyObject> mUsers;
Note: There are Android Studio plugins such as this one available to generate
Parcelable code
Serializable
Sending Activity Code
https://riptutorial.com/ 770
Getting a result from Activity to Fragment
Like Getting a result from another Activity you need to call the Fragment's method
startActivityForResult(Intent intent, int requestCode). note that you should not call
getActivity().startActivityForResult() as this will take the result back to the Fragment's parent
Activity.
Receiving the result can be done using the Fragment's method onActivityResult(). You need to
make sure that the Fragment's parent Activity also overrides onActivityResult() and calls it's super
implementation.
In the following example ActivityOne contains FragmentOne, which will start ActivityTwo and expect a
result from it.
ActivityOne
// You must override this method as the second Activity will always send its results to
this Activity and then to the Fragment
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
}
activity_one.xml
<fragment android:name="com.example.FragmentOne"
android:id="@+id/fragment_one"
android:layout_width="match_parent"
android:layout_height="match_parent" />
FragmentOne
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
https://riptutorial.com/ 771
if (requestCode == REQUEST_CODE && resultCode == RESULT_CODE) {
String testResult = data.getStringExtra(EXTRA_KEY_TEST);
// TODO: Do something with your extra data
}
}
}
ActivityTwo
https://riptutorial.com/ 772
Chapter 141: IntentService
Syntax
4. <service android:name=".UploadS3IntentService"android:exported="false" />
Remarks
An IntentService provides a simple way to offload work on a background thread. It handles
everything about receiving requests, putting them in a queue, stopping itself, etc. for you. It is also
easy to implement, making it the perfect thing to use when you have time-consuming operations to
do that don't belong on the Main (UI) thread.
Examples
Creating an IntentService
To create an IntentService, create a class which extends IntentService, and within it, a method
which overrides onHandleIntent:
package com.example.myapp;
public class MyIntentService extends IntentService {
@Override
protected void onHandleIntent (Intent workIntent) {
//Do something in the background, based on the contents of workIntent.
}
}
Here is an example of an IntentService that pretends to load images in the background. All you
need to do to implement an IntentService is to provide a constructor that calls the super(String)
constructor, and you need to implement the onHandleIntent(Intent) method.
/**
* Define a constructor and call the super(String) constructor, in order to name the
worker
* thread - this is important if you want to debug and know the name of the thread upon
* which this Service is operating its jobs.
*/
public ImageLoaderIntentService() {
super("Example");
}
https://riptutorial.com/ 773
@Override
protected void onHandleIntent(Intent intent) {
// This is where you do all your logic - this code is executed on a background thread
if (!TextUtils.isEmpty(imageUrl)) {
Drawable image = HttpUtils.loadImage(imageUrl); // HttpUtils is made-up for the
example
}
// Send your drawable back to the UI now, so that you can use it - there are many ways
// to achieve this, but they are out of reach for this example
}
}
In order to start an IntentService, you need to send an Intent to it. You can do so from an Activity,
for an example. Of course, you're not limited to that. Here is an example of how you would
summon your new Service from an Activity class.
The IntentService processes the data from its Intents sequentially, so that you can send multiple
Intents without worrying whether they will collide with each other. Only one Intent at a time is
processed, the rest go in a queue. When all the jobs are complete, the IntentService will shut itself
down automatically.
The abstract class IntentService is a base class for services, which run in the background without
any user interface. Therefore, in order to update the UI, we have to make use of a receiver, which
may be either a BroadcastReceiver or a ResultReceiver:
Within the IntentService, we have one key method, onHandleIntent(), in which we will do all
actions, for example, preparing notifications, creating alarms, etc.
If you want to use you own IntentService, you have to extend it as follows:
https://riptutorial.com/ 774
@Override
protected void onHandleIntent(Intent intent) {
// TODO: Write your own code here.
}
}
Similar to any activity, you can pass extra information such as bundle data to it as follows:
Now assume that we passed some data to the YourIntentService class. Based on this data, an
action can be performed as follows:
public YourIntentService () {
super("YourIntentService ");
}
@Override
protected void onHandleIntent(Intent intent) {
if(retrivedValue.equals(actvityValue)){
// Send the notification to foo.
} else {
// Retrieving data failed.
}
}
}
The code above also shows how to handle constraints in the OnHandleIntent() method.
https://riptutorial.com/ 775
Chapter 142: Inter-app UI testing with
UIAutomator
Syntax
• Instrumentation getInstrumentation()
• UIDevice UiDevice.getInstance(Instrumentation instrumentation)
• boolean UIDevice.pressHome()
• boolean UIDevice.pressBack()
• boolean UIDevice.pressRecentApps()
• void UIDevice.wakeUp()
• boolean UIDevice.swipe(int startX, int startY, int endX, int endY, int steps)
• boolean UIDevice.drag(int startX, int startY, int endX, int endY, int steps)
• UIObject2 UIDevice.findObject(By.desc(String contentDesc))
• boolean UIObject2.click()
Remarks
UIAutomator are especially good for testing user stories. You run into problems if view elements
have neither a unique resource-id nor content-desc. In most of the cases there is a way to
complete the test anyways, what that takes a lot of time. If you can influence the code of your app,
UIAutomator may be your testing tool.
Examples
Prepare your project and write the first UIAutomator test
Add the required libraries into the dependencies section of your Android module's build.gradle:
android {
...
defaultConfig {
...
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
dependencies {
...
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
androidTestCompile 'com.android.support:support-annotations:23.4.0'
}
Note that of course the versions may differ in the mean time.
https://riptutorial.com/ 776
After this sync with the changes.
@Override
public void setUp() throws Exception {
device = UiDevice.getInstance(getInstrumentation());
}
By making a right click on the class tab and on "Run "InterAppTest" executes this test.
In order to enable writing more complex UI tests the UIAutomatorViewer is needed. The tool
located at /tools/ makes a fullscreen screenshot including the layouts of the currently displayed
views. See the subsequent picture to get an idea of what is shown:
For the UI tests we are looking for resource-id, content-desc or something else to identify a view
and use it inside our tests.
https://riptutorial.com/ 777
If we now for instance want to click on the applications button and then open some app and swipe
around, this is how the test method can look like:
package de.androidtest.myapplication;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({InterAppTest1.class, InterAppTest2.class})
public class AppTestSuite {}
Execute similar to a single test by clicking right and run the suite.
https://riptutorial.com/ 778
Chapter 143: Interfaces
Examples
Custom Listener
Define interface
//In this interface, you can define messages, which will be send to owner.
public interface MyCustomListener {
//In this case we have two messages,
//the first that is sent when the process is successful.
void onSuccess(List<Bitmap> bitmapList);
//And The second message, when the process will fail.
void onFailure(String error);
}
Create listener
In the next step we need to define an instance variable in the object that will send callback via
MyCustomListener. And add setter for our listener.
Implement listener
Now, in other class, we can create instance of SampleClassB.
https://riptutorial.com/ 779
public class SomeActivity extends Activity implements MyCustomListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
SampleClassB sampleClass = new SampleClassB();
sampleClass.setMyCustomListener(this);
}
@Override
public void onSuccess(List<Bitmap> bitmapList) {
@Override
public void onFailure(String error) {
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
SampleClassB sampleClass = new SampleClassB();
sampleClass.setMyCustomListener(new MyCustomListener() {
@Override
public void onSuccess(List<Bitmap> bitmapList) {
@Override
public void onFailure(String error) {
}
});
}
}
Trigger listener
public class SampleClassB {
private MyCustomListener listener;
https://riptutorial.com/ 780
@Override
public void onDone(List<Bitmap> bitmapList, Exception e) {
//do some stuff if needed
Basic Listener
The "listener" or "observer" pattern is the most common strategy for creating asynchronous
callbacks in Android development.
@Override
protected void onCreate(Bundle savedInstanceState) {
https://riptutorial.com/ 781
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
https://riptutorial.com/ 782
Chapter 144: Internationalization and
localization (I18N and L10N)
Introduction
Internationalization (i18n) and Localization (L10n) are used to adapt software according to
differences in languages, regional differences and target audience.
Internationalization : the process of planning for future localization i.e. making the software design
flexible to an extent that it can adjust and adapt to future localization efforts.
Remarks
To test a device for localization, the device or the emulator can be rebooted in a particular locale
by using adb as follows :
e.g. to check Japanese localization in the app, use the command : setprop persist.sys.locale ja-
JP;stop;sleep 5;start
Examples
Planning for localization : enable RTL support in Manifest
RTL (Right-to-left) support is an essential part in planning for i18n and L10n. Unlike English
language which is written from left to right, many languages like Arabic, Japanese, Hebrew, etc.
are written from right to left. To appeal to a more global audience, it is a good idea to plan your
layouts to support these language from the very beginning of the project, so that adding
localization is easier later on.
RTL support can be enabled in an Android app by adding the supportsRtl tag in the
AndroidManifest, like so :
<application
...
android:supportsRtl="true"
...>
...
</application>
https://riptutorial.com/ 783
Planning for localization : Add RTL support in Layouts
Starting SDK 17 (Android 4.2), RTL support was added in Android layouts and is an essential part
of localization. Going forward, the left/right notation in layouts should be replaced by start/end
notation. If, however, your project has a minSdk value less than 17, then both left/right and
start/end notation should be used in layouts.
For relative layouts, alignParentStart and alignParentEnd should be used, like so:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
For specifying gravity and layout gravity, similar notation should be used, like so :
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|start"
android:gravity="left|start"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|end"
android:gravity="right|end"/>
<include layout="@layout/notification"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginStart="12dp"
android:paddingLeft="128dp"
android:paddingStart="128dp"
android:layout_toLeftOf="@id/cancel_action"
android:layout_toStartOf="@id/cancel_action"/>
<include layout="@layout/notification2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginRight="12dp"
android:layout_marginEnd="12dp"
android:paddingRight="128dp"
https://riptutorial.com/ 784
android:paddingEnd="128dp"
android:layout_toRightOf="@id/cancel_action"
android:layout_toEndOf="@id/cancel_action"/>
To test if the layouts that have been created are RTL compatible, do the following :
Go to Settings -> Developer options -> Drawing -> Force RTL layout direction
Enabling this option would force the device to use RTL locales and you can easily verify all parts
of the app for RTL support. Note that you don't need to actually add any new locales/ language
support up till this point.
The first step for coding for localization is to create default resources. This step is so implicit that
many developers do not even think about it. However, creating default resources is important
because if the device runs on an unsupported locale, it would load all of its resources from the
default folders. If even one of the resources is missing from the default folders, the app would
simply crash.
The default set of strings should be put in the following folder at the specified location:
res/values/strings.xml
This file should contain the strings in the language that majority users of the app are expected to
speak.
Also, default resources for the app should be placed at the following folders and locations :
res/drawable/
res/layout/
If your app requires folders like anim, or xml, the default resources should be added to the following
folders and locations:
res/anim/
res/xml/
res/raw/
res/values-<locale>/strings.xml
https://riptutorial.com/ 785
An example for the same is given below:
In this example, we have default English strings in the file res/values/strings.xml, French
translations are provided in the folder res/values-fr/strings.xml and Japanese translations are
provided in the folder res/values-ja/strings.xml
Other translations for other locales can similarly be added to the app.
A complete list of locale codes can be found here : ISO 639 codes
Non-translatable Strings:
Your project may have certain strings that are not to be translated. Strings which are used as keys
for SharedPreferences or strings which are used as symbols, fall in this category. These strings
should be stored only in the default strings.xml and should be marked with a translatable="false"
attribute. e.g.
This attribute is important because translations are often carried out by professionals who are
bilingual. This would allow these persons involved in translations to identify strings which are not
to be translated, thus saving time and money.
Creating language specific layouts is often unnecessary if you have specified the correct start/end
notation, as described in the earlier example. However, there may be situations where the defaults
layouts may not work correctly for certain languages. Sometimes, left-to-right layouts may not
translate for RTL languages. It is necessary to provide the correct layouts in such cases.
To provide complete optimization for RTL layouts, we can use entirely separate layout files using
the ldrtl resource qualifier (ldrtl stands for layout-direction-right-to-left}). For example, we can
save your default layout files in res/layout/ and our RTL optimized layouts in res/layout-ldrtl/.
The ldrtl qualifier is great for drawable resources, so that you can provide graphics that are
oriented in the direction corresponding to the reading direction.
https://riptutorial.com/ 786
Here is a great post which describes the precedence of the ldrtl layouts : Language specific
layouts
https://riptutorial.com/ 787
Chapter 145: Jackson
Introduction
Jackson is a multi-purpose Java library for processing JSON. Jackson aims to be the best
possible combination of fast, correct, lightweight, and ergonomic for developers.
Jackson features
Examples
Full Data Binding Example
JSON data
{
"name" : { "first" : "Joe", "last" : "Sixpack" },
"gender" : "MALE",
"verified" : false,
"userImage" : "keliuyue"
}
User.class
https://riptutorial.com/ 788
}
https://riptutorial.com/ 789
Chapter 146: Java on Android
Introduction
Android supports all Java 7 language features and a subset of Java 8 language features that vary
by platform version. This page describes the new language features you can use, how to properly
configure your project to use them and any known issues you may encounter.
Examples
Java 8 features subset with Retrolambda
Retrolambda lets you run Java 8 code with lambda expressions, method references and try-with-
resources statements on Java 7, 6 or 5. It does this by transforming your Java 8 compiled
bytecode so that it can run on an older Java runtime.
• Lambda expressions are backported by converting them to anonymous inner classes. This
includes the optimisation of using a singleton instance for stateless lambda expressions to
avoid repeated object allocation. Method references are basically just syntax sugar for
lambda expressions and they are backported in the same way.
• Objects.requireNonNull calls are replaced with calls to Object.getClass if the target bytecode
version is below Java 7. The synthetic null checks generated by JDK 9 use
Objects.requireNonNull, whereas earlier JDK versions used Object.getClass.
• Optionally also:
1. Default methods are backported by copying the default methods to a companion class
(interface name + "$") as static methods, replacing the default methods in the interface
with abstract methods, and by adding the necessary method implementations to all
classes which implement that interface.
Known Limitations:
https://riptutorial.com/ 790
• Backporting default methods and static methods on interfaces requires all backported
interfaces and all classes which implement them or call their static methods to be backported
together, with one execution of Retrolambda. In other words, you must always do a clean
build. Also, backporting default methods won't work across module or dependency
boundaries.
• May break if a future JDK 8 build stops generating a new class for each invokedynamic call.
Retrolambda works so that it captures the bytecode that java.lang.invoke.LambdaMetafactory
generates dynamically, so optimisations to that mechanism may break Retrolambda.
Retrolambda gradle plugin will automatically build your android project with Retrolambda. The
latest version can be found on the releases page.
Usage:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'me.tatarka:gradle-retrolambda:<latest version>'
}
}
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
Known Issues:
• Lint fails on java files that have lambdas. Android's lint doesn't understand java 8 syntax and
will fail silently or loudly. There is now an experimental fork that fixes the issue.
• Using Google Play Services causes Retrolambda to fail. Version 5.0.77 contains bytecode
that is incompatible with Retrolambda. This should be fixed in newer versions of play
services, if you can update, that should be the preferred solution. To work around this issue,
you can either use an earlier version like 4.4.52 or add -noverify to the jvm args.
https://riptutorial.com/ 791
retrolambda {
jvmArgs '-noverify'
}
https://riptutorial.com/ 792
Chapter 147: JCodec
Examples
Getting Started
You can get JCodec automatically with maven. For this just add below snippet to your pom.xml .
<dependency>
<groupId>org.jcodec</groupId>
<artifactId>jcodec-javase</artifactId>
<version>0.1.9</version>
</dependency>
Getting a single frame from a movie ( supports only AVC, H.264 in MP4, ISO BMF, Quicktime
container ):
Getting a sequence of frames from a movie ( supports only AVC, H.264 in MP4, ISO BMF,
Quicktime container ):
https://riptutorial.com/ 793
Chapter 148: Jenkins CI setup for Android
Projects
Examples
Step by step approach to set up Jenkins for Android
This is a step by step guide to set up the automated build process using Jenkins CI for your
Android projects. The following steps assume that you have new hardware with just any flavor of
Linux installed. It is also taken into account that you might have a remote machine.
wget https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz
4. Now you need to install Java 8 on your Ubuntu machine, which is a requirement for Android
builds on Nougat. Jenkins would require you to install JDK and JRE 7 using the steps below:
6. Download the latest supported Gradle version for your Android setup:
wget https://services.gradle.org/distributions/gradle-2.14.1-all.zip
https://riptutorial.com/ 794
unzip gradle-2.14.1-all.zip
7. Set up Android on your Ubuntu machine. First move to the tools folder in the Android SDK
folder downloaded in step 2:
9. Now log in to Jenkins using your internet browser. Type ipAddress:8080 into the address bar.
10. In order to receive the password for the first-time login, please check the corresponding file
as follows (you will need su permissions to access this file):
cat /var/lib/jenkins/secrets/initialAdminPassword
Name = JAVA_HOME
JAVA_HOME = /usr/lib/jvm/java-8-openjdk-amd64
3. Also add the following values to Git and save the environment variables:
Name = Default
/usr/bin/git
Name = ANDROID_HOME
Value = /home/username/android-sdk-linux
https://riptutorial.com/ 795
Part III: Create a Jenkins Job for your
Android project
1. Click on New Item in the Jenkins home screen.
3. In the General tab, select Advanced. Then select Use custom workspace:
Directory /home/user/Code/ProjectFolder
4. In the source code management select Git. I am using Bitbucket for the purpose of this
example:
Repository URL =
https://username:[email protected]/project/projectname.git
*/master
9. If you want to run Lint on the project, then add another build step into the Execute shell:
/home/user/gradle/gradle-2.14.1/bin/gradle lint
Now your system is finally set up to build Android projects using Jenkins. This setup makes your
life so much easier for releasing builds to QA and UAT teams.
PS: Since Jenkins is a different user on your Ubuntu machine, you should give it rights to create
folders in your workspace by executing the following command:
https://riptutorial.com/ 796
Chapter 149: Job Scheduling
Remarks
Beware of running lots of code or doing heavy work inside your JobService, for example in
onStartJob(). The code will run on the main/UI thread and therefore can lead to a blocked UI, no
longer responding app or even a crash of your app!
Because of that, you must offload the work, for example by using a Thread or AsyncTask.
Examples
Basic usage
@Override
public boolean onStartJob(JobParameters jobParameters) {
Log.i(TAG, "Job started");
@Override
public boolean onStopJob(JobParameters jobParameters) {
Log.w(TAG, "Job stopped");
return false;
}
}
https://riptutorial.com/ 797
Declare your MyJobService class as a new <service> element between <application> </application>
in your AndroidManifest.xml.
<service
android:name=".MyJobService"
android:permission="android.permission.BIND_JOB_SERVICE" />
</application>
</manifest>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
https://riptutorial.com/ 798
// schedule/start the job
int result = jobScheduler.schedule(jobInfo);
if (result == JobScheduler.RESULT_SUCCESS)
Log.d(TAG, "Successfully scheduled job: " + result);
else
Log.e(TAG, "RESULT_FAILURE: " + result);
}
After calling onButtonClick_startJob(), the job will approximately run in intervals of 10 seconds,
even when the app is in the paused state (user pressed home button and app is no longer visible).
Instead of cancelling all running jobs inside onButtonClick_stopJob(), you can also call
jobScheduler.cancel() to cancel a specific job based on it's job ID.
https://riptutorial.com/ 799
Chapter 150: JSON in Android with org.json
Syntax
• Object : An object is an unordered set of name/value pairs. An object begins with { (left
brace) and ends with } (right brace). Each name is followed by : (colon) and the name/value
pairs are separated by , (comma).
• Array : An array is an ordered collection of values. An array begins with [ (left bracket) and
ends with ] (right bracket). Values are separated by , (comma).
• Value : A value can be a string in double quotes, or a number, or true or false or null, or an
object or an array. These structures can be nested.
• Number : A number is very much like a C or Java number, except that the octal and
hexadecimal formats are not used.
Remarks
This topic is about using the org.json package that is included in the Android SDK.
Examples
Parse simple JSON object
{
"title": "test",
"content": "Hello World!!!",
"year": 2016,
"names" : [
"Hannah",
"David",
"Steve"
]
}
try {
// create a new instance from a string
JSONObject jsonObject = new JSONObject(jsonAsString);
String title = jsonObject.getString("title");
https://riptutorial.com/ 800
String content = jsonObject.getString("content");
int year = jsonObject.getInt("year");
JSONArray names = jsonObject.getJSONArray("names"); //for an array of String objects
} catch (JSONException e) {
Log.w(TAG,"Could not parse JSON. Error: " + e.getMessage());
}
{
"books":[
{
"title":"Android JSON Parsing",
"times_sold":186
}
]
}
Create the JSONObject using the empty constructor and add fields using the put() method, which is
overloaded so that it can be used with different types:
try {
// Create a new instance of a JSONObject
final JSONObject object = new JSONObject();
} catch (JSONException e) {
Log.e(TAG, "Failed to create JSONObject", e);
}
{
"name":"test",
"content":"Hello World!!!1",
https://riptutorial.com/ 801
"year":2016,
"value":3.23,
"member":true,
"null_value":null
}
try {
// Add the JSONArray to the JSONObject
obj.put("the_array", array);
} catch (JSONException e) {
e.printStackTrace();
}
{
"the_array":[
"ASDF",
"QWERTY"
]
}
If you need to produce a JSON string with a value of null like this:
{
"name":null
}
Functioning example:
jsonObject.put("name", JSONObject.NULL);
https://riptutorial.com/ 802
{
"some_string": null,
"ather_string": "something"
}
someString = "null";
/**
* According to http://stackoverflow.com/questions/18226288/json-jsonobject-optstring-returns-
string-null
* we need to provide a workaround to opt string from json that can be null.
* <strong></strong>
*/
public static String optNullableString(JSONObject jsonObject, String key) {
return optNullableString(jsonObject, key, "");
}
/**
* According to http://stackoverflow.com/questions/18226288/json-jsonobject-optstring-returns-
string-null
* we need to provide a workaround to opt string from json that can be null.
* <strong></strong>
*/
public static String optNullableString(JSONObject jsonObject, String key, String fallback) {
if (jsonObject.isNull(key)) {
return fallback;
} else {
return jsonObject.optString(key, fallback);
}
}
https://riptutorial.com/ 803
reads a JSON encoded value as a stream of tokens.
reader.beginArray();
while (reader.hasNext()) {
messages.add(readMessage(reader));
}
reader.endArray();
return messages;
}
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("id")) {
id = reader.nextLong();
} else if (name.equals("text")) {
text = reader.nextString();
} else if (name.equals("geo") && reader.peek() != JsonToken.NULL) {
geo = readDoublesArray(reader);
} else if (name.equals("user")) {
user = readUser(reader);
} else {
reader.skipValue();
}
}
reader.endObject();
return new Message(id, text, user, geo);
}
reader.beginArray();
while (reader.hasNext()) {
doubles.add(reader.nextDouble());
}
reader.endArray();
return doubles;
}
https://riptutorial.com/ 804
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("name")) {
username = reader.nextString();
} else if (name.equals("followers_count")) {
followersCount = reader.nextInt();
} else {
reader.skipValue();
}
}
reader.endObject();
return new User(username, followersCount);
}
To produce nested JSON object, you need to simply add one JSON object to another:
try {
requestObject.put("lastname", lastname);
requestObject.put("phone", phone);
requestObject.put("latitude", lat);
requestObject.put("longitude", lon);
requestObject.put("theme", theme);
requestObject.put("text", message);
mainObject.put("claim", requestObject);
} catch (JSONException e) {
return "JSON Error";
}
Now mainObject contains a key called claim with the whole requestObject as a value.
This is an example for how to handle dynamic key for response. Here A and B are dynamic keys it
can be anything
Response
{
"response": [
{
"A": [
{
"name": "Tango"
},
{
"name": "Ping"
}
],
https://riptutorial.com/ 805
"B": [
{
"name": "Jon"
},
{
"name": "Mark"
}
]
}
]
}
Java code
Sometimes it's useful to check if a field is present or absent on your JSON to avoid some
JSONException on your code.
To achieve that, use the JSONObject#has(String) or the method, like on the following example:
Sample JSON
{
"name":"James"
}
https://riptutorial.com/ 806
Java code
// This will be true, since the field "name" is present on our JSON.
if (json.has("name")) {
name = json.getString("name");
}
else {
name = "John";
}
// This will be false, since our JSON doesn't have the field "surname".
if (json.has("surname")) {
surname = json.getString("surname");
}
else {
surname = "Doe";
}
{
"student":{"name":"Rahul", "lastname":"sharma"},
"marks":{"maths":"88"}
}
To update the elements value in the json we need to assign the value and update.
try {
// Create a new instance of a JSONObject
final JSONObject object = new JSONObject(jsonString);
object.remove("student");
object.put("student",studentJSON);
} catch (JSONException e) {
Log.e(TAG, "Failed to create JSONObject", e);
}
updated value
https://riptutorial.com/ 807
"student":{"name":"Kumar", "lastname":"sharma"},
"marks":{"maths":"88"}
}
https://riptutorial.com/ 808
Chapter 151: Keyboard
Examples
Hide keyboard when user taps anywhere else on the screen
This would work for Fragment also, no need to add this code in Fragment.
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
View view = getCurrentFocus();
if (view != null && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() ==
MotionEvent.ACTION_MOVE) && view instanceof EditText &&
!view.getClass().getName().startsWith("android.webkit.")) {
int scrcoords[] = new int[2];
view.getLocationOnScreen(scrcoords);
float x = ev.getRawX() + view.getLeft() - scrcoords[0];
float y = ev.getRawY() + view.getTop() - scrcoords[1];
if (x < view.getLeft() || x > view.getRight() || y < view.getTop() || y >
view.getBottom())
((InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow((this
0);
}
return super.dispatchTouchEvent(ev);
}
The idea is to measure a layout before and after each change and if there is a significant change
you can be somewhat certain that its the softkeyboard.
https://riptutorial.com/ 809
mLastContentHeight = findViewById(Window.ID_ANDROID_CONTENT).getHeight();
rootView.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);
rootView.getViewTreeObserver().removeOnGlobalLayoutListener(keyboardLayoutListener);
https://riptutorial.com/ 810
Chapter 152: Layouts
Introduction
A layout defines the visual structure for a user interface, such as an activity or widget.
A layout is declared in XML, including screen elements that will appear in it. Code can be added to
the application to modify the state of screen objects at runtime, including those declared in XML.
Syntax
• android:gravity="top|bottom|left|right|center_vertical|fill_vertical|center_horizontal|fill_horizontal|cente
• android:layout_gravity="top|bottom|left|right|center_vertical|fill_vertical|center_horizontal|fill_horizont
Remarks
https://riptutorial.com/ 811
https://riptutorial.com/ 812
requires two layout passes to render properly. For complex view hierarchies, this can have a
significant impact on performance. Nesting RelativeLayouts makes this problem even worse,
because every RelativeLayout causes the number of layout passes to go up.
Examples
LinearLayout
The LinearLayout is a ViewGroup that arranges its children in a single column or a single row. The
orientation can be set by calling the method setOrientation() or using the xml attribute
android:orientation.
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/app_name" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@android:string/cancel" />
</LinearLayout>
https://riptutorial.com/ 813
2. Horizontal orientation : android:orientation="horizontal"
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/app_name" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@android:string/cancel" />
The LinearLayout also supports assigning a weight to individual children with the
android:layout_weight attribute.
RelativeLayout
RelativeLayout is a ViewGroup that displays child views in relative positions. By default, all child
https://riptutorial.com/ 814
views are drawn at the top-left of the layout, so you must define the position of each view using the
various layout properties available from RelativeLayout.LayoutParams. The value for each layout
property is either a boolean to enable a layout position relative to the parent RelativeLayout or an
ID that references another view in the layout against which the view should be positioned.
Example:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageView"
android:src="@mipmap/ic_launcher" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/editText"
android:layout_toRightOf="@+id/imageView"
android:layout_toEndOf="@+id/imageView"
android:hint="@string/hint" />
</RelativeLayout>
https://riptutorial.com/ 815
Gravity and layout gravity
android:layout_gravity
• android:layout_gravity is used to set the position of an element in its parent (e.g. a child View
inside a Layout).
• Supported by LinearLayout and FrameLayout
android:gravity
• android:gravity is used to set the position of content inside an element (e.g. a text inside a
TextView).
https://riptutorial.com/ 816
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical"
android:layout_gravity="left"
android:gravity="center_vertical">
<TextView
android:layout_width="@dimen/fixed"
android:layout_height="wrap_content"
android:text="@string/first"
android:background="@color/colorPrimary"
android:gravity="left"/>
<TextView
android:layout_width="@dimen/fixed"
android:layout_height="wrap_content"
android:text="@string/second"
android:background="@color/colorPrimary"
android:gravity="center"/>
<TextView
android:layout_width="@dimen/fixed"
android:layout_height="wrap_content"
android:text="@string/third"
android:background="@color/colorPrimary"
android:gravity="right"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center_vertical">
<TextView
android:layout_width="@dimen/fixed"
android:layout_height="wrap_content"
android:text="@string/first"
android:background="@color/colorAccent"
android:gravity="left"/>
<TextView
android:layout_width="@dimen/fixed"
android:layout_height="wrap_content"
android:text="@string/second"
android:background="@color/colorAccent"
android:gravity="center"/>
<TextView
https://riptutorial.com/ 817
android:layout_width="@dimen/fixed"
android:layout_height="wrap_content"
android:text="@string/third"
android:background="@color/colorAccent"
android:gravity="right"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical"
android:layout_gravity="right"
android:gravity="center_vertical">
<TextView
android:layout_width="@dimen/fixed"
android:layout_height="wrap_content"
android:text="@string/first"
android:background="@color/colorPrimaryDark"
android:gravity="left"/>
<TextView
android:layout_width="@dimen/fixed"
android:layout_height="wrap_content"
android:text="@string/second"
android:background="@color/colorPrimaryDark"
android:gravity="center"/>
<TextView
android:layout_width="@dimen/fixed"
android:layout_height="wrap_content"
android:text="@string/third"
android:background="@color/colorPrimaryDark"
android:gravity="right"/>
</LinearLayout>
</LinearLayout>
https://riptutorial.com/ 818
GridLayout
GridLayout, as the name suggests is a layout used to arrange Views in a grid. A GridLayout
divides itself into columns and rows. As you can see in the example below, the amount of columns
and/or rows is specified by the properties columnCount and rowCount. Adding Views to this layout will
add the first view to the first column, the second view to the second column, and the third view to
the first column of the second row.
https://riptutorial.com/ 819
android:columnCount="2"
android:rowCount="2">
<TextView
android:layout_width="@dimen/fixed"
android:layout_height="wrap_content"
android:text="@string/first"
android:background="@color/colorPrimary"
android:layout_margin="@dimen/default_margin" />
<TextView
android:layout_width="@dimen/fixed"
android:layout_height="wrap_content"
android:text="@string/second"
android:background="@color/colorPrimary"
android:layout_margin="@dimen/default_margin" />
<TextView
android:layout_width="@dimen/fixed"
android:layout_height="wrap_content"
android:text="@string/third"
android:background="@color/colorPrimary"
android:layout_margin="@dimen/default_margin" />
</GridLayout>
https://riptutorial.com/ 820
Percent Layouts
2.3
You can use the Percent Support Library by adding the following to your dependencies.
compile 'com.android.support:percent:25.3.1'
If you wanted to display a view that fills the screen horizontally but only half the screen vertically
you would do thie following.
<android.support.percent.PercentFrameLayout
https://riptutorial.com/ 821
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
app:layout_widthPercent="100%"
app:layout_heightPercent="50%"
android:background="@android:color/black" />
<android.support.percent.PercentFrameLayout>
You can also define the percentages in a separate XML file with code such as:
<fraction name="margin_start_percent">25%</fraction>
They also contain the ability to set a custom aspect ratio via app:layout_aspectRatio.
This allows you to set only a single dimension, such as only the width, and the height will be
automatically determined based on the aspect ratio you’ve defined, whether it is 4:3 or 16:9 or
even a square 1:1 aspect ratio.
For example:
<ImageView
app:layout_widthPercent="100%"
app:layout_aspectRatio="178%"
android:scaleType="centerCrop"
android:src="@drawable/header_background"/>
FrameLayout
FrameLayoutis designed to block out an area on the screen to display a single item. You can,
however, add multiple children to a FrameLayout and control their position within the FrameLayout
by assigning gravity to each child, using the android:layout_gravity attribute.
Generally, FrameLayout is used to hold a single child view. Common use cases are creating place
holders for inflating Fragments in Activity, overlapping views or applying foreground to the views.
Example:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:src="@drawable/nougat"
android:scaleType="fitCenter"
android:layout_height="match_parent"
android:layout_width="match_parent"/>
<TextView
https://riptutorial.com/ 822
android:text="FrameLayout Example"
android:textSize="30sp"
android:textStyle="bold"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:gravity="center"/>
</FrameLayout>
CoordinatorLayout
2.3
The CoordinatorLayout is a container somewhat similar to FrameLayout but with extra capabilities, it
is called super-powered FrameLayout in the official documentation.
https://riptutorial.com/ 823
In order to use it, you will first have to add a dependency for the support library in your gradle file:
compile 'com.android.support:design:25.3.1'
The number of the latest version of the library may be found here
One practical use case of the CoordinatorLayout is creating a view with a FloatingActionButton. In
this specific case, we will create a RecyclerView with a SwipeRefreshLayout and a
FloatingActionButton on top of that. Here's how you can do that:
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/recycler_view"/>
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:clickable="true"
android:color="@color/colorAccent"
android:src="@mipmap/ic_add_white"
android:layout_gravity="end|bottom"
app:layout_anchorGravity="bottom|right|end"/>
</android.support.design.widget.CoordinatorLayout>
2.3-2.3.2
An enclosing CoordinatorLayout can be used to achieve Material Design Scrolling Effects when
using inner layouts that support Nested Scrolling, such as NestedScrollView or RecyclerView.
https://riptutorial.com/ 824
• is used in the Toolbar properties
app:layout_scrollFlags="scroll|enterAlways"
• app:layout_behavior="@string/appbar_scrolling_view_behavior" is used in the ViewPager
properties
• A RecyclerView is used in the ViewPager Fragments
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="6dp">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:elevation="0dp"
app:layout_scrollFlags="scroll|enterAlways"
/>
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
app:tabMode="fixed"
android:layout_below="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
app:elevation="0dp"
app:tabTextColor="#d3d3d3"
android:minHeight="?attr/actionBarSize"
/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_below="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
</android.support.design.widget.CoordinatorLayout>
https://riptutorial.com/ 825
Result:
View Weight
One of the most used attribute for LinearLayout is the weight of its child views. Weight defines how
much space a view will consume compared to other views within a LinearLayout.
Weight is used when you want to give specific screen space to one component compared to other.
Key Properties:
• weightSumis the overall sum of weights of all child views. If you don't specify the weightSum,
the system will calculate the sum of all the weights on its own.
• layout_weight specifies the amount of space out of the total weight sum the widget will
occupy.
Code:
https://riptutorial.com/ 826
android:orientation="horizontal"
android:weightSum="4">
<EditText
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Type Your Text Here" />
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Text1" />
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Text1" />
</LinearLayout>
https://riptutorial.com/ 827
Now even if the size of the device is larger, the EditText will take 2/4 of the screen's space. Hence
the look of your app is seen consistent across all screens.
Note: Here the layout_width is kept 0dp as the widget space is divided horizontally. If the widgets
are to be aligned vertically layout_height will be set to 0dp. This is done to increase the efficiency of
the code because at runtime the system won't attempt to calculate the width or height respectively
as this is managed by the weight. If you instead used wrap_content the system would attempt to
calculate the width/height first before applying the weight attribute which causes another
calculation cycle.
Hierarchy
- LinearLayout(horizontal)
- ImageView
https://riptutorial.com/ 828
- LinearLayout(vertical)
- TextView
- TextView
Code
// for imageview
ImageView imageView = new ImageView(context);
// for horizontal linearlayout
LinearLayout linearLayout2 = new LinearLayout(context);
linearLayout2.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT));
linearLayout2.setOrientation(LinearLayout.VERTICAL);
LayoutParams
Every single ViewGroup (e.g. LinearLayout, RelativeLayout, CoordinatorLayout, etc.) needs to store
information about its children's properties. About the way its children are being laid out in the
ViewGroup. This information is stored in objects of a wrapper class ViewGroup.LayoutParams.
E.g. for
Most of ViewGroups reutilize the ability to set margins for their children, so they do not subclass
ViewGroup.LayoutParams directly, but they subclass ViewGroup.MarginLayoutParams instead (which itself
is a subclass of ViewGroup.LayoutParams).
LayoutParams in xml
LayoutParams objects are created based on the inflated layout xml file.
https://riptutorial.com/ 829
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_gravity="right"
android:gravity="bottom"
android:text="Example text"
android:textColor="@android:color/holo_green_dark"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/holo_green_dark"
android:scaleType="centerInside"
android:src="@drawable/example"/>
</LinearLayout>
All parameters that begin with layout_ specify how the enclosing layout should work. When the
layout is inflated, those parameters are wrapped in a proper LayoutParams object, that later will be
used by the Layout to properly position a particular View within the ViewGroup. Other attributes of a
View are directly View-related and are processed by the View itself.
For TextView:
For ImageView:
Because the LayoutParams object is directly related to the enclosing ViewGroup, this method will
return a non-null value only when View is attached to the ViewGroup. You need to bare in mind that
this object might not be present at all times. Especially you should not depend on having it inside
View's constructor.
https://riptutorial.com/ 830
setupView(context);
}
//...
}
If you want to depend on having LayoutParams object, you should use onAttachedToWindow method
instead.
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (getLayoutParams().height == 50) { // getLayoutParams() will NOT return null here
doSomething();
}
}
//...
}
You might need to use features that are specific to a particular ViewGroup (e.g. you might want to
programmatically change rules of a RelativeLayout). For that purpose you will need to know how to
properly cast the ViewGroup.LayoutParams object.
https://riptutorial.com/ 831
This might be a bit confusing when getting a LayoutParams object for a child View that actually is
another ViewGroup.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/outer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/inner_layout"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_gravity="right"/>
</LinearLayout>
IMPORTANT: The type of LayoutParams object is directly related to the type of the ENCLOSING
ViewGroup.
Incorrect casting:
Correct casting:
https://riptutorial.com/ 832
Chapter 153: Leakcanary
Introduction
Leak Canary is an Android and Java library used to detect leak in the application
Remarks
You can see the example in link below
https://github.com/square/leakcanary
Examples
Implementing a Leak Canary in Android Application
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
In your Application class you need to add the below code inside your onCreate():
LeakCanary.install(this);
That's all you need to do for LeakCanary, it will automatically show notifications when there is a
leak in your build.
https://riptutorial.com/ 833
Chapter 154: Library Dagger 2: Dependency
Injection in Applications
Introduction
Dagger 2, as explained on GitHub, is a compile-time evolution approach to dependency injection.
Taking the approach started in Dagger 1.x to its ultimate conclusion, Dagger 2.x eliminates all
reflection, and improves code clarity by removing the traditional ObjectGraph/Injector in favor of
user-specified @Component interfaces.
Remarks
1. Library setup in application(for maven, gradle,java projects)
2. Advantages of Dragger use
3. Important Links (for Documentation and demos)
4. How to integrate and use Dragger components
Dagger 2 API:
Dagger 2 exposes a number of special annotations:
Important Links:
GitHub: https://github.com/google/dagger
UserGuide(Google): https://google.github.io/dagger/users-guide.html
Videos: https://google.github.io/dagger/resources.html
https://riptutorial.com/ 834
Examples
Create @Module Class and @Singleton annotation for Object
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class VehicleModule {
@Provides @Singleton
Motor provideMotor(){
return new Motor();
}
@Provides @Singleton
Vehicle provideVehicle(){
return new Vehicle(new Motor());
}
}
Every provider (or method) must have the @Provides annotation and the class must have the
@Module annotation. The @Singleton annotation indicates that there will be only one instance of the
object.
Now that you have the providers for your different models, you need to request them. Just as
Vehicle needs Motor, you have to add the @Inject annotation in the Vehicle constructor as follows:
@Inject
public Vehicle(Motor motor){
this.motor = motor;
}
You can use the @Inject annotation to request dependencies in the constructor, fields, or methods.
In this example, I'm keeping the injection in the constructor.
The connection between the provider of dependencies, @Module, and the classes requesting them
through @Inject is made using @Component, which is an interface:
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = {VehicleModule.class})
public interface VehicleComponent {
Vehicle provideVehicle();
}
https://riptutorial.com/ 835
For the @Component annotation, you have to specify which modules are going to be used. In this
example VehicleModule is used, which is defined in this example. If you need to use more modules,
then just add them using a comma as a separator.
Now that you have every connection ready, you have to obtain an instance of this interface and
invoke its methods to obtain the object you need:
When you try to create a new object of the interface with the @Component annotation, you have to do
it using the prefix Dagger_<NameOfTheComponentInterface>, in this case Dagger_VehicleComponent, and
then use the builder method to call every module within.
https://riptutorial.com/ 836
Chapter 155: Lint Warnings
Remarks
The Lint tool checks your Android project source files for potential bugs and optimization
improvements for correctness, security, performance, usability, accessibility, and
internationalization. You can run Lint from the command-line or from Android Studio.
Official documentation:
https://developer.android.com/studio/write/lint.html
Examples
Using tools:ignore in xml files
The attribute tools:ignore can be used in xml files to dismiss lint warnings.
BUT dismissing lint warnings with this technique is most of the time the wrong way to
proceed.
A lint warning must be understood and fixed... it can be ignored if and only if you have a full
understanding of it's meaning and a strong reason to ignore it.
• You are developing a system-app (signed with the device manufacturer key)
• Your app need to change the device date (or any other protected action)
Then you can do this in your manifest : (i.e. requesting the protected permission and ignoring the
lint warning because you know that in your case the permission will be granted)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
...>
<uses-permission android:name="android.permission.SET_TIME"
tools:ignore="ProtectedPermissions"/>
Using the Android API 23 or higher, very often such situation can be seen:
https://riptutorial.com/ 837
This situation is caused by the structural change of the Android API regarding getting the
resources. Now the function:
public int getColor(@ColorRes int id, @Nullable Theme theme) throws NotFoundException
com.android.support:support-v4:24.0.0
ContextCompat.getColor(context, R.color.colorPrimaryDark);
ContextCompat.getDrawable(context, R.drawable.btn_check);
ContextCompat.getColorStateList(context, R.color.colorPrimary);
DrawableCompat.setTint(drawable);
ContextCompat.getColor(context,R.color.colorPrimaryDark));
ViewCompat.setElevation(textView, 1F);
ViewCompat.animate(textView);
TextViewCompat.setTextAppearance(textView, R.style.AppThemeTextStyle);
...
You can configure lint by adding a lintOptions section in the build.gradle file:
android {
//.....
lintOptions {
// turn off checking the given issue id's
disable 'TypographyFractions','TypographyQuotes'
https://riptutorial.com/ 838
You can run lint for a specific variant (see below), e.g. ./gradlew lintRelease, or for all variants (
./gradlew lint), in which case it produces a report which describes which specific variants a given
issue applies to.
Check here for the DSL reference for all available options.
You can specify your Lint checking preferences in the lint.xml file. If you are creating this file
manually, place it in the root directory of your Android project. If you are configuring Lint
preferences in Android Studio, the lint.xml file is automatically created and added to your Android
project for you.
Example:
By setting the severity attribute value in the tag, you can disable Lint checking for an issue or
change the severity level for an issue.
You can disable Lint checking from your Java and XML source files.
https://riptutorial.com/ 839
To disable Lint checking specifically for a Java class or method in your Android project, add the
@SuppressLint annotation to that Java code.
Example:
@SuppressLint("NewApi")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
@SuppressLint("all")
For example:
tools:ignore="NewApi,StringFormatInvalid"
To suppress checking for all Lint issues in the XML element, use
tools:ignore="all"
It's good practice to mark some warnings in your code. For example, some deprecated methods is
need for your testing, or old support version. But Lint checking will mark that code with warnings.
For avoiding this problem, you need use annotation @SuppressWarnings.
For example, add ignoring to warnings to deprecated methods. You need to put warnings
description in annotation also:
@SuppressWarnings("deprecated");
public void setAnotherColor (int newColor) {
getApplicationContext().getResources().getColor(newColor)
}
Using this annotation you can ignore all warnings, including Lint, Android, and other. Using
Suppress Warnings, helps to understand code correctly!
https://riptutorial.com/ 840
Chapter 156: ListView
Introduction
ListView is a viewgroup which groups several items from a data source like array or database and
displays them in a scroll-able list. Data are bound with listview using an Adapter class.
Remarks
ListView is a view group that displays a list of scrollable items.
The list items are automatically inserted to the list using an Adapter that pulls content from a source
such as an array or database query and converts each item result into a view that's placed into the
list.
When the content for your layout is dynamic or not pre-determined, you can use a layout that
subclasses AdapterView to populate the layout with views at runtime. A subclass of the AdapterView
class uses an Adapter to bind data to its layout.
Before using the ListView you should also checking the RecyclerView examples.
Examples
Filtering with CursorAdapter
Let's say your query will run every time the user types in an EditText:
https://riptutorial.com/ 841
EditText queryText = (EditText) findViewById(R.id.textQuery);
queryText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(final CharSequence s, final int start, final int count,
final int after) {
@Override
public void onTextChanged(final CharSequence s, final int start, final int before,
final int count) {
// This is the filter in action
adapter.getFilter().filter(s.toString());
// Don't forget to notify the adapter
adapter.notifyDataSetChanged();
}
@Override
public void afterTextChanged(final Editable s) {
}
});
Custom ArrayAdapter
By default the ArrayAdapter class creates a view for each array item by calling toString() on each
item and placing the contents in a TextView.
To create a complex view for each item (for example, if you want an ImageView for each array
item), extend the ArrayAdapter class and override the getView() method to return the type of View
you want for each item.
For example:
@Override
public long getItemId(int position)
{
//It is just an example
YourClassData data = (YourClassData) getItem(position);
return data.ID;
}
@Override
public View getView(int position, View view, ViewGroup parent)
{
ViewHolder viewHolder;
if (view == null) {
view = inflater.inflate(R.layout.custom_row_layout_design, null);
https://riptutorial.com/ 842
// Do some initialization
//Retrieve the view on the item layout and set the value.
viewHolder = new ViewHolder(view);
view.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.txt.setTypeface(m_Font);
viewHolder.txt.setText(data.text);
viewHolder.img.setImageBitmap(BitmapFactory.decodeFile(data.imageAddr));
return view;
By default the ArrayAdapter creates a view for each array item by calling toString() on each item
and placing the contents in a TextView.
Example:
where android.R.layout.simple_list_item_1 is the layout that contains a TextView for each string in
the array.
To use something other than TextViews for the array display, for instance, ImageViews, or to have
some of data besides toString() results fill the views, override getView(int, View, ViewGroup) to
return the type of view you want. Check this example.
https://riptutorial.com/ 843
Read ListView online: https://riptutorial.com/android/topic/4226/listview
https://riptutorial.com/ 844
Chapter 157: Loader
Introduction
Loader is good choice for prevent memory leak if you want to load data in background when
oncreate method is called. For example when we execute Asynctask in oncreate method and we
rotate the screen so the activity will recreate which will execute another AsyncTask again, so
probably two Asyntask running in parallel together rather than like loader which will continue the
background process we executed before.
Parameters
Class Description
Remarks
Introduced in Android 3.0, loaders make it easy to asynchronously load data in an activity or
fragment. Loaders have these characteristics:
https://riptutorial.com/ 845
have to run until completion, do not use Loaders. You should use services for this kind of stuff
instead.
Examples
Basic AsyncTaskLoader
@Override
public String loadInBackground() {
// Some work, e.g. load something from internet
return "OK";
}
@Override
public void deliverResult(String data) {
if (isStarted()) {
// Deliver result if loader is currently started
super.deliverResult(data);
}
}
@Override
protected void onStartLoading() {
// Start loading
forceLoad();
}
@Override
protected void onStopLoading() {
cancelLoad();
}
@Override
protected void onReset() {
super.onReset();
Typically Loader is initialized within the activity's onCreate() method, or within the fragment's
onActivityCreated(). Also usually activity or fragment implements LoaderManager.LoaderCallbacks
interface:
https://riptutorial.com/ 846
// Unique id for loader
private static final int LDR_BASIC_ID = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize loader; Some data can be passed as second param instead of Bundle.Empty
getLoaderManager().initLoader(LDR_BASIC_ID, Bundle.EMPTY, this);
}
@Override
public Loader<String> onCreateLoader(int id, Bundle args) {
return new BasicLoader(this);
}
@Override
public void onLoadFinished(Loader<String> loader, String data) {
Toast.makeText(this, data, Toast.LENGTH_LONG).show();
}
@Override
public void onLoaderReset(Loader<String> loader) {
}
}
In this example, when loader completed, toast with result will be shown.
It's a good practice to cache loaded result to avoid multiple loading of same data.
To invalidate cache onContentChanged() should be called. If loader has been already started,
forceLoad() will be called, otherwise (if loader in stopped state) loader will be able to understand
content change with takeContentChanged() check.
Take the current flag indicating whether the loader's content had changed while it was
stopped. If it had, true is returned and the flag is cleared.
@Override
public final void deliverResult(final T data) {
if (!isReset()) {
https://riptutorial.com/ 847
// Save loaded result
cache.set(data);
if (isStarted()) {
super.deliverResult(data);
}
}
}
@Override
protected final void onStartLoading() {
// Register observers
registerObserver();
@Override
public final void onStopLoading() {
cancelLoad();
}
@Override
protected final void onReset() {
super.onReset();
onStopLoading();
// Clear cache and remove observers
cache.set(null);
unregisterObserver();
}
/* virtual */
protected void registerObserver() {
// Register observers here, call onContentChanged() to invalidate cache
}
/* virtual */
protected void unregisterObserver() {
// Remove observers
}
}
Reloading
To invalidate your old data and restart existing loader you can use restartLoader() method:
https://riptutorial.com/ 848
You can pass parameters by Bundle:
@Override
public Loader<String> onCreateLoader(int id, final Bundle args) {
final String myParam = args.getString(MY_KEY);
...
}
https://riptutorial.com/ 849
Chapter 158: Loading Bitmaps Effectively
Introduction
This Topic Mainly Concentrate on Loading the Bitmaps Effectively in Android Devices.
When it comes to loading a bitmap, the question comes where it is loaded from. Here we are
going to discuss about how to load the Bitmap from Resource with in the Android Device. i.e. eg
from Gallery.
Syntax
• <uses-permission> -- > Tag Used for the Permission .
• android:name -- > An attribute used to give name for the permission we are going to request.
• android.permission.READ_EXTERNAL_STORAGE --> It is System permissions
• example "android.permission.CAMERA" or "android.permission.READ_CONTACTS"
Examples
Load the Image from Resource from Android Device. Using Intents.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
https://riptutorial.com/ 850
https://riptutorial.com/ 851
https://riptutorial.com/android/topic/10902/loading-bitmaps-effectively
https://riptutorial.com/ 852
Chapter 159: Localization with resources in
Android
Examples
Currency
You have to create a different strings.xml file for every new language.
strings.xml
<resources>
<string name="app_name">Testing Application</string>
<string name="hello">Hello World</string>
</resources>
strings.xml(hi)
<resources>
<string name="app_name">परीक्षण आवेदन</string>
<string name="hello">नमस्ते दुनिया</string>
</resources>
https://riptutorial.com/ 853
config.setLocale(myLocale);
} else {
config.locale = myLocale;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
getBaseContext().createConfigurationContext(config);
} else {
getBaseContext().getResources().updateConfiguration(config,
getBaseContext().getResources().getDisplayMetrics());
}
}
The function above will change the text fields which are referenced from strings.xml. For example,
assume that you have the following two text views:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello"/>
Then, after changing the locale, the language strings having the ids app_name and hello will be
changed accordingly.
When localizing different types of resources are required, each of which has its own home in the
android project structure. Following are the different directories that we can place under the \res
directory. The resource types placed in each of these directories are explained in the table below:
XML files that define tween animations. (Property animations can also be saved
anim/ in this directory, but the animator/ directory is preferred for property animations to
distinguish between the two types.)
color/ XML files that define a state list of colors. See Color State List Resource
"Bitmap files (.png, .9.png, .jpg, .gif) or XML files that are compiled into the
drawable/ following drawable resource subtypes: : Bitmap files - Nine-Patches (re-sizable
bitmaps) - State lists - Shapes - Animation drawables - Other drawables - "
Drawable files for different launcher icon densities. For more information on
mipmap/ managing launcher icons with mipmap/ folders, see Managing Projects
Overview.
layout/ XML files that define a user interface layout. See Layout Resource.
https://riptutorial.com/ 854
Directory Resource Type
XML files that define application menus, such as an Options Menu, Context
menu/
Menu, or Sub Menu. See Menu Resource.
Arbitrary files to save in their raw form. To open these resources with a
raw/ raw InputStream, call Resources.openRawResource() with the resource ID,
which is R.raw.filename.
However, if you need access to original file names and file hierarchy, you might
consider saving some resources in the assets/ directory (instead ofres/raw/).
Files in assets/ are not given a resource ID, so you can read them only
using AssetManager.
XML files that contain simple values, such as strings, integers, and colors, as
values/
well as styles and themes
Configuration types and qualifier names for each folder under the "res"
directory
Each resource directory under the res folder (listed in the example above) can have different
variations of the contained resources in similarly named directory suffixed with different qualifier-
values for each configuration-type.
Example of variations of `` directory with different qualifier values suffixed which are often seen in
our android projects:
• drawable/
• drawable-en/
• drawable-fr-rCA/
• drawable-en-port/
• drawable-en-notouch-12key/
• drawable-port-ldpi/
• drawable-port-notouch-12key/
mcc310
https://riptutorial.com/ 855
Configuration Qualifier Values
mcc310-mnc004
mcc208-mnc00
etc.
en
fr
en-rUS
fr-rFR
fr-rCA
ldltr
smallestWidth swdp
Examples:
sw320dp
sw600dp
sw720dp
w720dp
w1024dp
h720dp
h1024dp
normal
large
https://riptutorial.com/ 856
Configuration Qualifier Values
xlarge
notlong
notround
land
UI mode car
desk
television
appliancewatch
notnight
mdpi
hdpi
xhdpi
xxhdpi
xxxhdpi
nodpi
tvdpi
anydpi
finger
https://riptutorial.com/ 857
Configuration Qualifier Values
keyshidden
keyssoft
qwerty
12key
navhidden
dpad
trackball
wheel
v3
v4
v7
In above examples you understand how to localize resources of application. Following example
explain how to change the application locale within application, not from device. In order to change
Application locale only, you can use below locale util.
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.preference.PreferenceManager;
import android.view.ContextThemeWrapper;
import java.util.Locale;
/**
* Created by Umesh on 10/10/16.
*/
public class LocaleUtils {
https://riptutorial.com/ 858
private static Locale mLocale;
SharedPreferences.Editor editor =
PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putString("lang_code",mPrefLangCode);
editor.commit();
}
https://riptutorial.com/ 859
SharedPreferences.Editor editor =
PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putString("country_code",mPrefCountryCode);
editor.commit();
}
}
@Override
public void onCreate() {
super.onCreate();
LocaleUtils.setLocale(new Locale(LocaleUtils.getPrefLangCode(this),
LocaleUtils.getPrefCountryCode(this)));
LocaleUtils.updateConfiguration(this, getResources().getConfiguration());
}
}
You also need to create a base activity and extend this activity to all other activity so that you can
change locale of application only one place as follows :
public LocalizationActivity() {
LocaleUtils.updateConfiguration(this);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Note: When you change locale of application programmatically, need to restart your
activity to take the effect of locale change In order to work properly for this solution you
https://riptutorial.com/ 860
and use locale from shared preferences on app startup you android:name=".LocaleApp"
in you Manifest.xml.
Sometimes Lint checker prompt to create the release build. To solve such issue follow below
options.
First:
If you want to disable translation for some strings only then add following attribute to default
string.xml
Second:
Ignore all missing translation from resource file add following attribute It's the ignore attribute of
the tools namespace in your strings file, as follows:
</resources>
Third:
http://tools.android.com/recent/non-translatablestrings
If you have a lot of resources that should not be translated, you can place them in a file named
donottranslate.xml and lint will consider all of them non-translatable resources.
Fourth:
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:locale="en" tools:ignore="MissingTranslation">
You can also disable missing translation check for lint from app/build.gradle
lintOptions {
disable 'MissingTranslation'
}
https://riptutorial.com/ 861
Read Localization with resources in Android online:
https://riptutorial.com/android/topic/3345/localization-with-resources-in-android
https://riptutorial.com/ 862
Chapter 160: Localized Date/Time in Android
Remarks
It is recommended to use methods of the DateUtils class in order to format dates which are locale
aware, i.e. which consider user preferences (e.g. 12h/24h clock time formats). These methods are
most appropriate for dates that are displayed to the user.
For fully customized date representations, it is recommended to use the SimpleDateFormat class,
as it allows to fully control all date elements.
Examples
Custom localized date format with DateUtils.formatDateTime()
DateUtils.formatDateTime() allows you to supply a time, and based on the flags you provide, it
creates a localized datetime string. The flags allow you to specify whether to include specific
elements (like the weekday).
Format a date:
Format a date and time. Date is in short format, time is in long format:
https://riptutorial.com/ 863
• HH: hour (0-23)
• hh: hour (1-12)
• a: AM/PM marker
• mm: minute (0-59)
• ss: second
• dd: day in month (1-31)
• MM: month
• yyyy: year
https://riptutorial.com/ 864
Chapter 161: Location
Introduction
Android Location APIs are used in a wide variety of apps for different purposes such as finding
user location, notifying when a user has left a general area (Geofencing), and help interpret user
activity (walking, running, driving, etc).
However, Android Location APIs are not the only means of acquiring user location. The following
will give examples of how to use Android's LocationManager and other common location libraries.
Remarks
For building Location aware apps in Android, there are two paths:
LocationManager
Pros
Cons
Features
• NMEA Listener
• GPS Status Listener
• Listen to provider status changes (ex. GPS is turned off by user)
• List of providers to choose location source from
Providers
GPS
• Permissions Required:
○ ACCESS_FINE_LOCATION
• Accuracy: 10m - 100m
https://riptutorial.com/ 865
• Power Requirements: HIGH
• Availability: Worldwide (with clear view of the sky)
• NOTES:
○Location updates typically come in once every second, but in situations where GPS
has not been used for some time and A-GPS is unavailable, it make take several
minutes for a location to be received.
○In cases where clear view of the sky is obstructed, GPS points will not cluster very well
(location points "jump") and accuracy may be misleading in certain areas due to the "
Urban Canyon" effect.
Network
• Permissions Required:
○ ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION
• Accuracy: 100m - 1000m+
• Power Requirements: LOW - MEDIUM
• Availability: Within range of cell tower or wifi signal
• NOTES:
○ Location updates occur less frequently than GPS
○ Location updates typically do not cluster well (location points "jump") and accuracy can
range depending number of different factors (number of wifi signals, signal strength,
type of cell tower, etc.)
Passive
• Permissions Required:
○ ACCESS_FINE_LOCATION
• Accuracy: 10m - 1000m+
• Power Requirements: NONE
• Availability: Only when another app receives a location from either GPS or Network
• NOTES:
○ Don't rely on this to give you continuous updates. This listens passively to other apps
that make location requests, and passes those locations back.
○ Does not return FusedLocationProviderApi generated points, only the underlying
location points used to generate them.
FusedLocationProviderApi
Pros
Cons
https://riptutorial.com/ 866
• Less granular control over GPS
• May not be available on all devices or in certain countries
• Requires third party library dependency
Features
PRIORITY_HIGH_ACCURACY
• Permissions Required:
○ ACCESS_FINE_LOCATION for more accurate location or ACCESS_COARSE_LOCATION for less
accurate location
• Accuracy: 10m - 100m
• Power Requirements: HIGH
• Availability: Wherever Google Play Services is available.
• NOTES:
○ If ACCESS_FINE_LOCATION is not used, this will not use GPS for generating location
updates, but will still find a fairly accurate point in the right conditions.
○ If ACCESS_FINE_LOCATION is used, it may or may not use GPS to generate location points,
depending on how accurate it can currently track the device given the environmental
conditions.
○ Although this may report more accurate location updates than the other settings, it is
still prone to the "Urban Canyon" effect.
PRIORITY_BALANCED_POWER_ACCURACY
• Permissions Required:
○ ACCESS_FINE_LOCATION for more accurate location or ACCESS_COARSE_LOCATION for less
accurate location
• Accuracy: 100m - 1000m+
• Power Requirements: MEDIUM
• Availability: Wherever Google Play Services is available.
• NOTES:
○ Same notes as PRIORITY_HIGH_ACCURACY
○ Although it is unlikely, this setting may still use GPS to generate a location.
PRIORITY_LOW_POWER
• Permissions Required:
○ ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION
• Accuracy: 100m - 1000m+
• Power Requirements: LOW
https://riptutorial.com/ 867
• Availability: Wherever Google Play Services is available.
• NOTES:
○Probably does not use GPS, but untested thus far.
○Updates are typically not very accurate
○Used generally to detect significant changes in location
PRIORITY_NO_POWER
• Permissions Required:
○ ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION
• Accuracy: 10m - 1000m+
• Power Requirements: NONE
• Availability: Wherever Google Play Services is available.
• NOTES:
○ Functions almost identically to LocationManager PASSIVE_PROVIDER
○ Reports back Google Play Services updates when received, where PASSIVE_PROVIDER
reports back underlying location updates used
TroubleShooting
OnLocationChanged() Never Called
Since this seems to be a common issue with getting Android Locations, I'll put down a quick
checklist of common fixes:
One of the most common issues is that the right permissions were never given. If you are
using GPS (with or without Network), use <uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"/>, else use <uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION"/>. Google's FusedLocationApi
requires ACCESS_FINE_LOCATION.
Check for and request permissions! If you are never given permissions, you'll end up with
crashes, or worse (if you are catching all exceptions), you'll end up with no indication of
anything! It doesn't matter if the user grants you permission at the start of the app, always
check to see if you have permissions for all calls. The user can easily go to their settings and
revoke them.
Are you sure you are passing in the right listener? Did you add that BroadcastReceiver or
IntentService to your manifest? Are you using PendingIntent.getService() on a
https://riptutorial.com/ 868
BroadcastReceiver class, or getBroadcast() on an IntentService class? Are you sure you are
not unregistering your listener somewhere else in your code immediately after requesting?
https://riptutorial.com/ 869
https://riptutorial.com/ 870
If you are using Network services, did you turn on "Scanning Always Available"? Do you
have your location mode set to "Best" ("High Accuracy") or "Battery Saving" ("Network
Only")?
https://riptutorial.com/ 871
https://riptutorial.com/ 872
If you are using GPS, did you turn on "Best" ("High Accuracy") or "Device only" in location
mode?
https://riptutorial.com/ 873
https://riptutorial.com/ 874
Yes, this is on here twice. Did you try using a LocationListener instead of a PendingIntent, or
vice-versa, to ensure you actually implemented LocationManager properly? Are you sure that
the location request isn't being removed in some part of the Activity or Service lifecycle that
you didn't expect to happen?
, or vice-versa, to ensure you actually implemented LocationManager properly? Are you sure
that the location request isn't being removed in some part of the Activity or Service lifecycle
that you didn't expect to happen?
Are you testing GPS on the first floor of a building in the middle of San Francisco? Are you
testing Network locations in the middle of nowhere? Do you work in a secret underground
bunker void of all radio signals, wondering why your device isn't getting location? Always
double check your surroundings when trying to troubleshoot location problems!
There could be many other less obvious reasons why location isn't working, but before searching
out those esoteric fixes, just run through this quick checklist.
Examples
Fused location API
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
https://riptutorial.com/ 875
.setInterval(2000) //At least once every 2 seconds
.setFastestInterval(1000); //At most once a second
}
@Override
protected void onStart(){
super.onStart();
mGoogleApiClient.connect();
}
@Override
protected void onResume(){
super.onResume();
//Permission check for Android 6.0+
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
if(mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, this);
}
}
}
@Override
protected void onPause(){
super.onPause();
//Permission check for Android 6.0+
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
if(mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient,
this);
}
}
}
@Override
protected void onStop(){
super.onStop();
mGoogleApiClient.disconnect();
}
@Override
public void onConnected(@Nullable Bundle bundle) {
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, this);
}
}
@Override
public void onConnectionSuspended(int i) {
mGoogleApiClient.connect();
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
}
@Override
https://riptutorial.com/ 876
public void onLocationChanged(Location location) {
//Handle your location update code here
}
}
/*
* This example is useful if you have many different classes that should be
* receiving location updates, but want more granular control over which ones
* listen to the updates.
*
* For example, this activity will stop getting updates when it is not visible, but a database
* class with a registered local receiver will continue to receive updates, until
"stopUpdates()" is called here.
*
*/
public class ExampleActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
@Override
protected void onResume(){
super.onResume();
@Override
protected void onPause(){
super.onPause();
https://riptutorial.com/ 877
//Helper method to stop updates
private void stopUpdates(){
startService(new Intent(this, LocationService.class).putExtra("remove", true));
}
/*
* Internal receiver used to get location updates for this activity.
*
* This receiver and any receiver registered with LocalBroadcastManager does
* not need to be registered in the Manifest.
*
*/
private static class InternalLocationReceiver extends BroadcastReceiver{
InternalLocationReceiver(ExampleActivity activity){
mActivity = activity;
}
@Override
public void onReceive(Context context, Intent intent) {
final ExampleActivity activity = mActivity;
if(activity != null) {
LocationResult result = intent.getParcelableExtra("result");
//Handle location update here
}
}
}
}
LocationService
@Override
public void onCreate(){
super.onCreate();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
@Override
public int onStartCommand(Intent intent, int flags, int startId){
https://riptutorial.com/ 878
super.onStartCommand(intent, flags, startId);
//Permission check for Android 6.0+
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
if (intent.getBooleanExtra("request", false)) {
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, getPendingIntent());
} else {
mGoogleApiClient.connect();
}
}
else if(intent.getBooleanExtra("remove", false)){
stopSelf();
}
}
return START_STICKY;
}
@Override
public void onDestroy(){
super.onDestroy();
if(mGoogleApiClient.isConnected()){
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient,
getPendingIntent());
mGoogleApiClient.disconnect();
}
}
@Override
public void onConnected(@Nullable Bundle bundle) {
//Permission check for Android 6.0+
if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, getPendingIntent());
}
}
@Override
public void onConnectionSuspended(int i) {
mGoogleApiClient.connect();
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
https://riptutorial.com/ 879
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
LocationReceiver
@Override
public void onReceive(Context context, Intent intent) {
if(LocationResult.hasResult(intent)){
LocationResult locationResult = LocationResult.extractResult(intent);
LocalBroadcastManager.getInstance(context).sendBroadcast(new
Intent("googleLocation").putExtra("result", locationResult));
}
}
}
As always, you need to make sure you have the required permissions.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
@Override
protected void onResume() {
super.onResume();
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
}
catch(SecurityException e){
// The app doesn't have the correct permissions
}
}
@Override
protected void onPause() {
try{
mLocationManager.removeUpdates(this);
https://riptutorial.com/ 880
}
catch (SecurityException e){
// The app doesn't have the correct permissions
}
super.onPause();
}
@Override
public void onLocationChanged(Location location) {
// We received a location update!
Log.i("onLocationChanged", location.toString());
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
@Override
public void onProviderEnabled(String provider) {
@Override
public void onProviderDisabled(String provider) {
}
}
As always, you need to make sure you have the required permissions.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
@Override
protected void onResume() {
super.onResume();
mLocationHandlerThread.start();
https://riptutorial.com/ 881
mLocationHandlerLooper = mLocationHandlerThread.getLooper();
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this,
mLocationHandlerLooper);
}
catch(SecurityException e){
// The app doesn't have the correct permissions
}
}
@Override
protected void onPause() {
try{
mLocationManager.removeUpdates(this);
}
catch (SecurityException e){
// The app doesn't have the correct permissions
}
mLocationHandlerLooper = null;
mLocationHandlerThread = null;
super.onPause();
}
@Override
public void onLocationChanged(Location location) {
// We received a location update on a separate thread!
Log.i("onLocationChanged", location.toString());
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
@Override
public void onProviderEnabled(String provider) {
@Override
public void onProviderDisabled(String provider) {
https://riptutorial.com/ 882
}
Register geofence
GeoFenceObserversationService.java:
@Override
public void onCreate() {
super.onCreate();
mInstant = this;
mGeofenceList = new ArrayList<Geofence>();
mSharedPreferences = getSharedPreferences(AppConstants.SHARED_PREFERENCES_NAME,
MODE_PRIVATE);
mGeofencesAdded = mSharedPreferences.getBoolean(AppConstants.GEOFENCES_ADDED_KEY,
false);
buildGoogleApiClient();
}
@Override
public void onDestroy() {
mGoogleApiClient.disconnect();
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
https://riptutorial.com/ 883
mGoogleApiClient.connect();
}
@Override
public void onConnected(Bundle connectionHint) {
}
@Override
public void onConnectionFailed(ConnectionResult result) {
}
@Override
public void onConnectionSuspended(int cause) {
populateGeofenceList();
if(!mGeofenceList.isEmpty()){
try {
LocationServices.GeofencingApi.addGeofences(mGoogleApiClient,
getGeofencingRequest(), getGeofencePendingIntent()).setResultCallback(this);
} catch (SecurityException securityException) {
securityException.printStackTrace();
}
}
LocationServices.GeofencingApi.removeGeofences(mGoogleApiClient,getGeofencePendingIntent()).setResultCa
https://riptutorial.com/ 884
if (status.isSuccess()) {
mGeofencesAdded = !mGeofencesAdded;
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putBoolean(AppConstants.GEOFENCES_ADDED_KEY, mGeofencesAdded);
editor.apply();
} else {
String errorMessage = AppConstants.getErrorString(this,status.getStatusCode());
Log.i("Geofence", errorMessage);
}
}
AppConstant:
• startService(new Intent(getApplicationContext(),GeoFenceObserversationService.class));
• GeoFenceObserversationService.getInstant().addGeofences();
https://riptutorial.com/ 885
Get Address From Location using Geocoder
After you got the Location object from FusedAPI, you can easily acquire Address information from
that object.
@Override
public void onReceive(Context context, Intent intent) {
if (LocationResult.hasResult(intent)) {
LocationResult locationResult = LocationResult.extractResult(intent);
Location location = locationResult.getLastLocation();
if (location != null) {
// Do something with your location
} else {
Log.d(LocationReceiver.class.getSimpleName(), "*** location object is null
***");
}
}
}
}
@Override
public void onConnected(Bundle connectionHint) {
Intent backgroundIntent = new Intent(this, LocationReceiver.class);
https://riptutorial.com/ 886
mBackgroundPendingIntent = backgroundPendingIntent.getBroadcast(getApplicationContext(),
LOCATION_REUEST_CODE, backgroundIntent, PendingIntent.FLAG_CANCEL_CURRENT);
mFusedLocationProviderApi.requestLocationUpdates(mLocationClient, mLocationRequest,
backgroundPendingIntent);
}
Don't forget to remove the location update intent in the appropriate lifecycle callback:
@Override
public void onDestroy() {
if (servicesAvailable && mLocationClient != null) {
if (mLocationClient.isConnected()) {
fusedLocationProviderApi.removeLocationUpdates(mLocationClient,
backgroundPendingIntent);
// Destroy the current location client
mLocationClient = null;
} else {
mLocationClient.unregisterConnectionCallbacks(this);
mLocationClient = null;
}
}
super.onDestroy();
}
https://riptutorial.com/ 887
Chapter 162: Logging and using Logcat
Syntax
• Log.v(String tag, String msg, Throwable tr)
• Log.v(String tag, String msg)
• Log.d(String tag, String msg, Throwable tr)
• Log.d(String tag, String msg)
• Log.i(String tag, String msg, Throwable tr)
• Log.i(String tag, String msg)
• Log.w(String tag, String msg, Throwable tr)
• Log.w(String tag, String msg)
• Log.e(String tag, String msg, Throwable tr)
• Log.e(String tag, String msg)
Parameters
Option Description
Loads an alternate log buffer for viewing, such as events or radio. The main
-b (buffer)
buffer is used by default. See Viewing Alternative Log Buffers.
-f
Writes log message output to (filename). The default is stdout.
(filename)
Sets the maximum number of rotated logs to (count). The default value is 4.
-n (count)
Requires the -r option.
Rotates the log file every (kbytes) of output. The default value is 16. Requires
-r (kbytes)
the -f option.
-v (format) Sets the output format for log messages. The default is brief format.
Remarks
Definition
https://riptutorial.com/ 888
Logcat is a command-line tool that dumps a log of system messages, including stack traces when
the device throws an error and messages that you have written from your app with the Log class.
When to use
If you are considering using Java's System.out methods for printing to the console instead of using
one of Android's Log methods, then you should know that they basically work the same. However,
it is better to avoid using Java's methods because the extra information and formatting provided by
Android's Log methods are more beneficial. Also, the System.out print methods are redirected to
the Log.i() method.
Useful links
• Android developer official documentation for Log and logcat.
• Stackoveflow Question : Android Log.v(), Log.d(), Log.i(), Log.w(), Log.e() - When to use
each one?
Examples
Filtering the logcat output
It is helpful to filter the logcat output because there are many messages which are not of interest.
To filter the output, open the "Android Monitor" and click on the drop down on the top-right and
select Edit Filter Configuration
https://riptutorial.com/ 889
Now you can add custom filters to show messages which are of interest, as well as filter out well-
known log lines which can safely be ignored. To ignore a part of the output you may define a
Regular Expression. Here is an example of excluding matching tags:
^(?!(HideMe|AndThis))
https://riptutorial.com/ 890
The above is a regular expression which excludes inputs. If you wanted to add another tag to the
blacklist, add it after a pipe | character. For example, if you wanted to blacklist "GC", you would
use a filter like this:
^(?!(HideMe|AndThis|GC))
For more documentation and examples visit Logging and using Logcat
Logging
Any quality Android application will keep track of what it's doing through application logs. These
logs allow easy debugging help for the developer to diagnose what's going on with the application.
Full Android Documentation can be found here, but a summary follows:
Basic Logging
The Log class is the main source of writing developer logs, by specifying a tag and a message. The
tag is what you can use to filter log messages by to identify which lines come from your particular
Activity. Simply call
https://riptutorial.com/ 891
07-28 12:00:00.759 24812-24839/my.packagename V/MyAnimator: Some log messages
└ time stamp | app.package┘ | └ any tag |
process & thread ids ┘ log level┘ └ the log message
TIP:
Notice the process id and the thread id. If they are the same - the log is coming from
the main/UI thread!
Any tag can be used, but it is common to use the class name as a tag:
Log Levels
The Android logger has 6 different levels, each of which serve a certain purpose:
• ERROR: Log.e()
○ Used to indicate critical failure, this is the level printed at when throwing an Exception.
• WARN: Log.w()
○ Used to indicate a warning, mainly for recoverable failures
• INFO: Log.i()
○Used to indicate higher-level information about the state of the application
• DEBUG: Log.d()
○Used to log information that would be useful to know when debugging the application,
but would get in the way when running the application
• VERBOSE: Log.v()
○Used to log information that reflects the small details about the state of the application
• ASSERT: Log.wtf()
○Used to log information about a condition that should never happen.
○wtf stands for "What a Terrible Failure".
Followed by a bunch of stack traces that would eventually lead to the offending line, where
stepping through with a debugger would eventually lead to the problem
However, the log trace of an application with logging enabled could look something like this:
https://riptutorial.com/ 892
V/MyApplication: Looking for file myFile.txt on the SD card
D/MyApplication: Found file myFile.txt at path <path>
V/MyApplication: Opening file myFile.txt
D/MyApplication: Finished reading myFile.txt, found 0 lines
V/MyApplication: Closing file myFile.txt
...
E/MyApplication: Process: com.example.myapplication, PID: 25788
com.example.SomeRandomException: Expected string, got 'null' instead
A quick glance at the logs and it is obvious that the file was empty.
Log Readability:
It is common for Android Applications to have several logs running synchronously. As such, it is
very important that each log is easily readable and only contains relevant, necessary information.
Performance:
Logging does require a small amount of system resources. In general, this does not warrant
concern, however, if overused, logging may have a negative impact on application performance.
Security:
Recently, several Android Applications have been added to the Google Play marketplace that
allow the user to view logs of all running applications. This unintended display of data may allow
users to view confidential information. As a rule of thumb, always remove logs that contain on non-
public data before publishing your application to the marketplace.
Conclusion:
Logging is an essential part of an Android application, because of the power it gives to developers.
The ability to create a useful log trace is one of the most challenging aspects of software
development, but Android's Log class helps to make it much easier.
For more documentation and examples visit Logging and using Logcat
This is a nice trick to add a link to code, so it will be easy to jump to the code that issued the log.
https://riptutorial.com/ 893
With the following code, this call:
MyLogger.logWithLink("MyTag","param="+param);
This is a basic example, it can be easily extended to issue a link to the caller (hint: the stack will be
[4] instead of [3]), and you can also add other relevant information.
Logcat is a command-line tool that dumps a log of system messages, including stack traces when
the device throws an error and messages that you have written from your app with the Log class.
The Logcat output can be displayed within Android Studio's Android Monitor or with adb command
line.
In Android Studio
https://riptutorial.com/ 894
Or by pressing Alt+6 on Windows/Linux or CMD+6 on Mac.
Simple usage:
$ adb logcat
With timestamps:
There are many options and filters available to command line logcat, documented here.
A simple but useful example is the following filter expression that displays all log messages with
priority level "error", on all tags:
Android Studio's Live templates can offer quite a few shortcuts for quick logging.
To use Live templates, all you need to do is to start typing the template name, and hit TAB or enter
to insert the statement.
Examples:
Full list of templates can be found in Android Studio's settings (ALT+s and type "live"). And it is
possible to add your custom templates as well.
If you find Android Studio's Live templates not enough for your needs, you can consider Android
Postfix Plugin
This is a very useful library which helps you to avoid writing the logging line manually.
.log - Logging. If there is constant variable "TAG", it use "TAG" . Else it use class name.
https://riptutorial.com/ 895
Android Studio usage
https://riptutorial.com/ 896
3. Disable/enable opening log window when starting run/debug application
https://riptutorial.com/ 897
Clear logs
adb logcat -c
https://riptutorial.com/ 898
Chapter 163: Looper
Introduction
A Looper is an Android class used to run a message loop for a thread, which usually do not have
one associated with them.
The most common Looper in Android is the main-loop, also commonly known as the main-thread.
This instance is unique for an application and can be accessed statically with
Looper.getMainLooper().
If a Looper is associated with the current thread, it can be retrieved with Looper.myLooper().
Examples
Create a simple LooperThread
A typical example of the implementation of a Looper thread given by the official documentation
uses Looper.prepare() and Looper.loop() and associates a Handler with the loop between these
calls.
Looper.loop();
}
}
A HandlerThread can be used to start a thread with a Looper. This looper then can be used to create
a Handler for communications with it.
https://riptutorial.com/ 899
Chapter 164: LruCache
Remarks
You should use Lru Cache in applications where repetitive resource loads would affect a smooth
app behaviour. For example a photo gallery with big thumbnails (128x128).
Always be careful with the size of the Lru cache since setting it too high might affect the app.
After the Lru Cache is no longer useful avoid holding any references to it to allow the Garbage
Collector to clean it up from memory.
For the best performance remember to load resources like bitmaps using the best practices like
selecting a proper inSampleSize before adding it to the Lru cache.
Examples
Initialising the cache
The Lru Cache will store all the added resources (values) for fast access until it reaches a memory
limit, in which case it will drop the less used resource (value) to store the new one.
To initialise the Lru cache you need to provide a maximum memory value. This value depends on
your application requirements and in how critical the resource is to keep a smooth app usage. A
recommended value for an image gallery, for example, would be 1/8 of your maximum available
memory.
Also note that the Lru Cache works on a key-value basis. In the following example, the key is a
String and the value is a Bitmap:
To add a resource to the cache you must provide a key and the resource. First make sure that the
value is not in the cache already
https://riptutorial.com/ 900
Getting a Bitmap(Resouce) from the cache
To get a resource from the cache simply pass the key of your resource (String in this example)
https://riptutorial.com/ 901
Chapter 165: Material Design
Introduction
Material Design is a comprehensive guide for visual, motion, and interaction design across
platforms and devices.
Remarks
Also see the original Android blog post introducing the Design Support Library
Official Documentation
https://developer.android.com/design/material/index.html
https://material.io/guidelines
https://design.google.com/resources/
Examples
Apply an AppCompat theme
The AppCompat support library provides themes to build apps with the Material Design
specification. A theme with a parent of Theme.AppCompat is also required for an Activity to extend
AppCompatActivity.
The first step is to customize your theme’s color palette to automatically colorize your app.
In your app's res/styles.xml you can define:
<!-- your app branding color for the app bar -->
<item name="colorPrimary">#2196f3</item>
<!-- darker variant for the status bar and contextual app bars -->
<item name="colorPrimaryDark">#1976d2</item>
Instead of Theme.AppCompat, which has a dark background, you can also use Theme.AppCompat.Light
https://riptutorial.com/ 902
or Theme.AppCompat.Light.DarkActionBar.
You can customize the theme with your own colours. Good choices are in the Material design
specification colour chart, and Material Palette. The "500" colours are good choices for primary
(blue 500 in this example); choose "700" of the same hue for the dark one; and an a shade from a
different hue as the accent colour. The primary colour is used for your app's toolbar and its entry in
the overview (recent apps) screen, the darker variant to tint the status bar, and the accent colour
to highlight some controls.
After creating this theme, apply it to your app in the AndroidManifest.xml and also apply the theme
to any particular activity. This is useful for applying a AppTheme.NoActionBar theme, which lets you
implement non-default toolbar configurations.
<application android:theme="@style/AppTheme"
...>
<activity
android:name=".MainActivity"
android:theme="@style/AppTheme" />
</application>
You can also apply themes to individual Views using android:theme and a ThemeOverlay theme. For
example with a Toolbar:
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
or a Button:
<Button
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="@style/MyButtonTheme"/>
Adding a Toolbar
A Toolbar is a generalization of ActionBar for use within application layouts. While an ActionBar is
traditionally part of an Activity's opaque window decor controlled by the framework, a Toolbar
may be placed at any arbitrary level of nesting within a view hierarchy. It can be added by
performing the following steps:
1. Make sure the following dependency is added to your module's (e.g. app's) build.gradle file
under dependencies:
https://riptutorial.com/ 903
compile 'com.android.support:appcompat-v7:25.3.1'
2. Set the theme for your app to one that does not have an ActionBar. To do that, edit your
styles.xml file under res/values, and set a Theme.AppCompat theme.
In this example we are using Theme.AppCompat.NoActionBar as parent of your AppTheme:
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"/>
Below the Toolbar you can add the rest of your layout.
4. In your Activity, set the Toolbar as the ActionBar for this Activity. Provided that you're using
the appcompat library and an AppCompatActivity, you would use the setSupportActionBar()
method:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//...
}
After performing the above steps, you can use the getSupportActionBar() method to manipulate the
Toolbar that is set as the ActionBar.
getSupportActionBar().setTitle("Activity Title");
For example, you can also set title and background color as shown below:
https://riptutorial.com/ 904
s.setSpan(new ForegroundColorSpan(Color.RED), 0, title.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
getSupportActionBar().setTitle(s);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.argb(128, 0, 0, 0)));
In the material design, a Floating action button represents the primary action in an Activity.
They are distinguished by a circled icon floating above the UI and have motion behaviors that
include morphing, launching, and a transferring anchor point.
Make sure the following dependency is added to your app's build.gradle file under dependencies:
compile 'com.android.support:design:25.3.1'
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="@drawable/some_icon"/>
where the src attribute references the icon that should be used for the floating action.
The result should look something like this (presuming your accent color is Material Pink):
By default, the background color of your FloatingActionButton will be set to your theme's accent
color. Also, note that a FloatingActionButton requires a margin around it to work properly. The
recommended margin for the bottom is 16dp for phones and 24dp for tablets.
Here are properties which you can use to customize the FloatingActionButton further (assuming
xmlns:app="http://schemas.android.com/apk/res-auto is declared as namespace the top of your
layout):
• app:fabSize: Can be set to normal or mini to switch between a normal sized or a smaller
version.
• app:rippleColor:Sets the color of the ripple effect of your FloatingActionButton. Can be a
color resource or hex string.
• app:elevation: Can be a string, integer, boolean, color value, floating point, dimension value.
• app:useCompatPadding: Enable compat padding. Maybe a boolean value, such as true or false.
Set to true to use compat padding on api-21 and later, in order to maintain a consistent look
with older api levels.
https://riptutorial.com/ 905
You can find more examples about FAB here.
The AppCompat Support Library defines several useful styles for Buttons, each of which extend a
base Widget.AppCompat.Button style that is applied to all buttons by default if you are using an
AppCompat theme. This style helps ensure that all buttons look the same by default following the
Material Design specification.
<Button
style="@style/Widget.AppCompat.Button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/simple_button"/>
<Button
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/colored_button"/>
If you want to customize the background color without changing the accent color in your
main theme you can create a custom theme (extending the ThemeOverlay theme) for your
Button and assign it to the button's android:theme attribute:
<Button
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
https://riptutorial.com/ 906
android:theme="@style/MyButtonTheme"/>
<Button
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/borderless_button"/>
<Button
style="@style/Widget.AppCompat.Button.Borderless.Colored"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/borderless_colored_button"/>
Make sure the following dependency is added to your app's build.gradle file under dependencies:
compile 'com.android.support:design:25.3.1'
Show the hint from an EditText as a floating label when a value is entered.
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
https://riptutorial.com/ 907
android:hint="@string/form_username"/>
</android.support.design.widget.TextInputLayout>
For displaying the password display eye icon with TextInputLayout, we can make use of the
following code:
<android.support.design.widget.TextInputLayout
android:id="@+id/input_layout_current_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:passwordToggleEnabled="true">
<android.support.design.widget.TextInputEditText
android:id="@+id/current_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/current_password"
android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>
You can find more details and examples in the dedicated topic.
Adding a TabLayout
TabLayout provides a horizontal layout to display tabs, and is commonly used in conjunction with
a ViewPager.
Make sure the following dependency is added to your app's build.gradle file under dependencies:
compile 'com.android.support:design:25.3.1'
Now you can add items to a TabLayout in your layout using the TabItem class.
For example:
<android.support.design.widget.TabLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/tabLayout">
<android.support.design.widget.TabItem
android:text="@string/tab_text_1"
android:icon="@drawable/ic_tab_1"/>
<android.support.design.widget.TabItem
android:text="@string/tab_text_2"
https://riptutorial.com/ 908
android:icon="@drawable/ic_tab_2"/>
</android.support.design.widget.TabLayout>
@Override
public void onTabUnselected(TabLayout.Tab tab) {
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
tabLayout.removeTab(tab);
tabLayout.removeTabAt(0);
tabLayout.removeAllTabs();
tabLayout.setTabMode(TabLayout.MODE_FIXED);
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed|scrollable" />
Note: the TabLayout modes are mutually exclusive, meaning only one can be active at a time.
The tab indicator color is the accent color defined for your Material Design theme.
You can override this color by defining a custom style in styles.xml and then applying the style to
https://riptutorial.com/ 909
your TabLayout:
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
style="@style/MyCustomTabLayoutStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.design.widget.TabLayout>
RippleDrawable
Ripple touch effect was introduced with material design in Android 5.0 (API level 21) and the
animation is implemented by the new RippleDrawable class.
Drawable that shows a ripple effect in response to state changes. The anchoring
position of the ripple for a given state may be specified by calling setHotspot(float x,
float y) with the corresponding state attribute identifier.
5.0
In general, ripple effect for regular buttons works by default in API 21 and above, and for other
touchable views, it can be achieved by specifying:
android:background="?android:attr/selectableItemBackground">
android:background="?android:attr/selectableItemBackgroundBorderless"
https://riptutorial.com/ 910
(Image courtesy: http://blog.csdn.net/a396901990/article/details/40187203 )
https://riptutorial.com/ 911
int backgroundResource = typedArray.getResourceId(0, 0);
myView.setBackgroundResource(backgroundResource);
Ripples can also be added to a view using the android:foreground attribute the same way as
above. As the name suggests, in case the ripple is added to the foreground, the ripple will show up
above any view it is added to (e.g. ImageView, a LinearLayout containing multiple views, etc).
If you want to customize the ripple effect into a view, you need to create a new XML file, inside the
drawable directory.
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#ffff0000" />
<ripple android:color="#7777777"
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/mask"
android:drawable="#ffff00" />
<item android:drawable="@android:color/white"/>
</ripple>
If there is view with a background already specified with a shape, corners and any other tags, to add
a ripple to that view use a mask layer and set the ripple as the background of the view.
Example:
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#ff0000ff">
<item android:drawable="@drawable/my_drawable" />
</ripple>
Usage: To attach your ripple xml file to any view, set it as background as following (assuming your
https://riptutorial.com/ 912
ripple file is named my_ripple.xml):
<View
android:id="@+id/myViewId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/my_ripple" />
Selector:
The ripple drawable can also be used in place of color state list selectors if your target version is
v21 or above (you can also place the ripple selector in the drawable-v21 folder):
<!--/drawable-v21/button.xml:-->
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight">
<item android:drawable="@drawable/button_normal" />
</ripple>
In this case, the color of the default state of your view would be white and the pressed state would
show the ripple drawable.
Point to note: Using ?android:colorControlHighlight will give the ripple the same color as the built-
in ripples in your app.
To change just the ripple color, you can customize the color android:colorControlHighlight in your
theme like so:
</resources>
and then use this theme in your activities, etc. The effect would be like the image below:
https://riptutorial.com/ 913
(Image courtesy: http://blog.csdn.net/a396901990/article/details/40187203 )
https://riptutorial.com/ 914
Make sure that you have added design support library in your build.gradle file under
dependencies:
dependencies {
// ...
compile 'com.android.support:design:25.3.1'
}
Next, add the DrawerLayout and NavigationView in your XML layout resource file.
The DrawerLayout is just a fancy container that allows the NavigationView, the actual navigation
drawer, to slide out from the left or right of the screen. Note: for mobile devices, the standard
drawer size is 320dp.
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.NavigationView
android:id="@+id/navigation_drawer"
android:layout_width="320dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/drawer_header"
app:menu="@menu/navigation_menu" />
</android.support.v4.widget.DrawerLayout>
https://riptutorial.com/ 915
Now, if you wish, create a header file that will serve as the top of your navigation drawer. This is
used to give a much more elegant look to the drawer.
<ImageView
android:id="@+id/header_image"
android:layout_width="140dp"
android:layout_height="120dp"
android:layout_centerInParent="true"
android:scaleType="centerCrop"
android:src="@drawable/image" />
<TextView
android:id="@+id/header_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/header_image"
android:text="User name"
android:textSize="20sp" />
</RelativeLayout>
To automatically populate your navigation drawer with material design-compliant navigation items,
create a menu file and add items as needed. Note: while icons for items aren't required, they are
suggested in the Material Design specification.
It is referenced in the NavigationView tag in the app:menu="@menu/navigation_menu" attribute.
https://riptutorial.com/ 916
android:id="@+id/nav_item_4"
android:title="Item #4"
android:icon="@drawable/ic_nav_4" />
</menu>
To separate items into groups, put them into a <menu> nested in another <item> with an
android:title attribute or wrap them with the <group> tag.
You can now do whatever you want in the header view of the NavigationView
The header view behaves like any other View, so once you use findViewById() and add some other
Views to your layout file, you can set the properties of anything in it.
You can find more details and examples in the dedicated topic.
Bottom sheets slide up from the bottom of the screen to reveal more content.
They were added to the Android Support Library in v25.1.0 version and supports above all the
versions.
https://riptutorial.com/ 917
Make sure the following dependency is added to your app's build.gradle file under dependencies:
compile 'com.android.support:design:25.3.1'
<android.support.design.widget.CoordinatorLayout >
<LinearLayout
android:id="@+id/bottom_sheet"
android:elevation="4dp"
android:minHeight="120dp"
app:behavior_peekHeight="120dp"
...
app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
You can set the state of your BottomSheetBehavior using the setState() method:
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
• STATE_COLLAPSED:this collapsed state is the default and shows just a portion of the layout
along the bottom. The height can be controlled with the app:behavior_peekHeight attribute
(defaults to 0)
• STATE_EXPANDED: the fully expanded state of the bottom sheet, where either the whole bottom
sheet is visible (if its height is less than the containing CoordinatorLayout) or the entire
CoordinatorLayout is filled
https://riptutorial.com/ 918
Further to open or close the BottomSheet on click of a View of your choice, A Button let's say,
here is how to toggle the sheet behavior and update view.
But BottomSheet behavior also has a feature where user can interact with the swipe UP or Down it
with a DRAG motion. In such a case, we might not be able to update the dependent View (like the
button above) If the Sheet state has changed. For that matter, you’d like to receive callbacks of
state changes, hence you can add a BottomSheetCallback to listen to user swipe events:
mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
// React to state change and notify views of the current state
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
// React to dragging events and animate views or transparency of dependent views
}
});
And if you only want your Bottom Sheet to be visible only in COLLAPSED and EXPANDED mode
toggles and never HIDE use:
mBottomSheetBehavior2.setHideable(false);
Within the setupDialog() method, you can inflate a new layout file and retrieve the
https://riptutorial.com/ 919
BottomSheetBehavior of the container view in your Activity. Once you have the behavior, you can
create and associate a BottomSheetCallback with it to dismiss the Fragment when the sheet is
hidden.
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
dismiss();
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
};
@Override
public void setupDialog(Dialog dialog, int style) {
super.setupDialog(dialog, style);
View contentView = View.inflate(getContext(), R.layout.fragment_bottom_sheet, null);
dialog.setContentView(contentView);
Finally, you can call show() on an instance of your Fragment to display it in the bottom sheet.
Add a Snackbar
One of the main features in Material Design is the addition of a Snackbar, which in theory replaces
the previous Toast. As per the Android documentation:
Snackbars contain a single line of text directly related to the operation performed. They
may contain a text action, but no icons. Toasts are primarily used for system
messaging. They also display at the bottom of the screen, but may not be swiped off-
https://riptutorial.com/ 920
screen.
Toasts can still be used in Android to display messages to users, however if you have decided to
opt for material design usage in your app, it is recommended that you actually use a snackbar.
Instead of being displayed as an overlay on your screen, a Snackbar pops from the bottom.
As for the length of time to show the Snackbar, we have the options similar to the ones offered by a
Toast or we could set a custom duration in milliseconds:
• LENGTH_SHORT
• LENGTH_LONG
• LENGTH_INDEFINITE
• setDuration() (since version 22.2.1)
You can also add dynamic features to your Snackbar such as ActionCallback or custom color.
However do pay attention to the design guideline offered by Android when customising a Snackbar.
Implementing the Snackbar has one limitation however. The parent layout of the view you are going
to implement a Snackbar in needs to be a CoordinatorLayout. This is so that the actual popup from
https://riptutorial.com/ 921
the bottom can be made.
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</android.support.design.widget.CoordinatorLayout>
The CoordinatorLayout then needs to be defined in your Activity's onCreate method, and then used
when creating the Snackbar itself.
For more information about about the Snackbar, please check the official documentation or the
dedicated topic in the documentation.
https://riptutorial.com/ 922
Chapter 166: MediaPlayer
Syntax
• void setAudioStreamType(int streamtype)
• void setDataSource(Context context, Uri uri)
• void prepare()
• void prepareAsync()
• void start()
• void stop()
Remarks
The MediaPlayer usage is mainly based on the State diagram:
https://riptutorial.com/ 923
That means that in order to play audio/video a defined sequence of action must occur is specific
order. It also states what actions can be made in which state.
The MediaPlayer API lacks flexibility (adding custom decoder and rendering logic) and lacks
sSupport for Dynamic Adaptive Streaming over HTTP (DASH) and SmoothStreaming. For these,
look into ExoPlayer.
https://riptutorial.com/ 924
Examples
Basic creation and playing
MediaPlayer class can be used to control playback of audio/video files and streams.
Asynchronous prepare
The MediaPlayer$prepare() is a blocking call and will freeze the UI till execution completes. To solve
this problem, MediaPlayer$prepareAsync() can be used.
On synchronous operations, errors would normally be signaled with an exception or an error code,
but whenever you use asynchronous resources, you should make sure your application is notified
of errors appropriately. For MediaPlayer,
mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener(){
https://riptutorial.com/ 925
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// ... react appropriately ...
// The MediaPlayer has moved to the Error state, must be reset!
// Then return true if the error has been handled
}
});
return alarms;
}
The list depends on the types of requested ringtones. The possibilities are:
• RingtoneManager.TYPE_RINGTONE
• RingtoneManager.TYPE_NOTIFICATION
• RingtoneManager.TYPE_ALARM
• RingtoneManager.TYPE_ALL = TYPE_RINGTONE | TYPE_NOTIFICATION | TYPE_ALARM
In order to get the Ringtones as android.media.Ringtone every Uri must be resolved by the
RingtoneManager:
https://riptutorial.com/ 926
from android.media.MediaPlayer. MediaPlayer must be initialised and prepared according to the State
diagram
Every example here is written for AudioManager.STREAM_RING stream type. However this is not the
only one. The available stream types are:
• STREAM_ALARM
• STREAM_DTMF
• STREAM_MUSIC
• STREAM_NOTIFICATION
• STREAM_RING
• STREAM_SYSTEM
• STREAM_VOICE_CALL
Setting volume
To get the volume of specific profile, call:
This value is very little useful, when the maximum value for the stream is not known:
The ratio of those two value will give a relative volume (0 < volume < 1):
https://riptutorial.com/ 927
To make the volume for the stream lower by one step, call:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tool_sound);
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
progress_bar = (ProgressBar) findViewById(R.id.progress_bar);
btn_play_stop.setEnabled(false);
btn_play_stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mediaPlayer.isPlaying()) {
mediaPlayer.pause();
btn_play_stop.setImageResource(R.drawable.ic_pause_black_24dp);
} else {
mediaPlayer.start();
btn_play_stop.setImageResource(R.drawable.ic_play_arrow_black_24px);
}
}
});
mediaPlayer.setDataSource(proxyUrl);
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
observer.stop();
progress_bar.setProgress(mp.getCurrentPosition());
// TODO Auto-generated method stub
mediaPlayer.stop();
mediaPlayer.reset();
}
https://riptutorial.com/ 928
});
mediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
progress_bar.setSecondaryProgress(percent);
}
});
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
btn_play_stop.setEnabled(true);
}
});
observer = new MediaObserver();
mediaPlayer.prepare();
mediaPlayer.start();
new Thread(observer).start();
}
@Override
public void run() {
while (!stop.get()) {
progress_bar.setProgress((int)((double)mediaPlayer.getCurrentPosition() /
(double)mediaPlayer.getDuration()*100));
try {
Thread.sleep(200);
} catch (Exception ex) {
Logger.log(ToolSoundActivity.this, ex);
}
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mediaPlayer.stop();
}
}
<LinearLayout
android:gravity="bottom"
android:layout_gravity="bottom"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:weightSum="1">
https://riptutorial.com/ 929
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageButton
app:srcCompat="@drawable/ic_play_arrow_black_24px"
android:layout_width="48dp"
android:layout_height="48dp"
android:id="@+id/btn_play_stop" />
<ProgressBar
android:padding="8dp"
android:progress="0"
android:id="@+id/progress_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</LinearLayout>
</LinearLayout>
This is an example how to get the play an audio file which you already have on your pc/laptop
.First create a new directory under res and name it as raw like this
copy the audio which you want to play into this folder .It may be a .mp3 or .wav file.
Now for example on button click you want to play this sound ,here is how it is done
https://riptutorial.com/ 930
public class MainActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.aboutapp_activity);
Button button=(Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
song.start();
}
});
}
}
This will play the song only once when the button is clicked,if you want to replay the song on every
button click write code like this
Button button=(Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (song.isPlaying()) {
song.reset();
song= MediaPlayer.create(getApplicationContext(), R.raw.song);
}
song.start();
}
});
}
}
https://riptutorial.com/ 931
Chapter 167: MediaSession
Syntax
• void mediaSessionCompat.setFlags(int flags)
• void mediaSessionCompat.setMediaButtonReceiver(PendingIntent mbr)
• void mediaSessionCompat.setCallback(MediaSessionCompat.Callback callback)
• void mediaSessionCompat.setActive(boolean active)
• MediaSessionCompat.Token mediaSessionCompat.getSessionToken()
• void mediaSessionCompat.release()
• void mediaSessionCompat.setPlaybackState(PlaybackStateCompat state)
• void mediaSessionCompat.setMetadata(MediaMetadataCompat metadata)
Remarks
For best practice, use the media-compat library. The library takes care of backward compatibility
by translating media session methods to the equivalent methods on older platform versions when
available.
Examples
Receiving and handling button events
This example creates a MediaSession object when a Service is started. The MediaSession object is
released when the Service gets destroyed:
@Override
public void onCreate() {
// Instantiate new MediaSession object.
configureMediaSession();
}
@Override
public void onDestroy() {
if (s_mediaSession != null)
s_mediaSession.release();
}
}
The following method instantiates and configures the MediaSession button callbacks:
https://riptutorial.com/ 932
s_mediaSession.setCallback(new MediaSession.Callback() {
@Override
public boolean onMediaButtonEvent(Intent mediaButtonIntent) {
Log.d(TAG, "onMediaButtonEvent called: " + mediaButtonIntent);
KeyEvent ke = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (ke != null && ke.getAction() == KeyEvent.ACTION_DOWN) {
int keyCode = ke.getKeyCode();
Log.d(TAG, "onMediaButtonEvent Received command: " + ke);
}
return super.onMediaButtonEvent(mediaButtonIntent);
}
@Override
public void onSkipToNext() {
Log.d(TAG, "onSkipToNext called (media button pressed)");
Toast.makeText(getApplicationContext(), "onSkipToNext called",
Toast.LENGTH_SHORT).show();
skipToNextPlaylistItem(); // Handle this button press.
super.onSkipToNext();
}
@Override
public void onSkipToPrevious() {
Log.d(TAG, "onSkipToPrevious called (media button pressed)");
Toast.makeText(getApplicationContext(), "onSkipToPrevious called",
Toast.LENGTH_SHORT).show();
skipToPreviousPlaylistItem(); // Handle this button press.
super.onSkipToPrevious();
}
@Override
public void onPause() {
Log.d(TAG, "onPause called (media button pressed)");
Toast.makeText(getApplicationContext(), "onPause called",
Toast.LENGTH_SHORT).show();
mpPause(); // Pause the player.
super.onPause();
}
@Override
public void onPlay() {
Log.d(TAG, "onPlay called (media button pressed)");
mpStart(); // Start player/playback.
super.onPlay();
}
@Override
public void onStop() {
Log.d(TAG, "onStop called (media button pressed)");
mpReset(); // Stop and/or reset the player.
super.onStop();
}
});
s_mediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
s_mediaSession.setActive(true);
}
The following method sends meta data (stored in a HashMap) to the device using A2DP:
https://riptutorial.com/ 933
void sendMetaData(@NonNull final HashMap<String, String> hm) {
// Return if Bluetooth A2DP is not in use.
if (!((AudioManager) getSystemService(Context.AUDIO_SERVICE)).isBluetoothA2dpOn()) return;
s_mediaSession.setMetadata(metadata);
}
The following method sets the PlaybackState. It also sets which button actions the MediaSession will
respond to:
s_mediaSession.setPlaybackState(state);
}
https://riptutorial.com/ 934
Chapter 168: MediaStore
Examples
Fetch Audio/MP3 files from specific folder of device or fetch all files
First, add the following permissions to the manifest of your project in order to enable device
storage access:
Then, create the file AudioModel.class and put the following model class into it in order to allow
getting and setting list items:
Next, use the following method to read all MP3 files from a folder of your device or to read all files
of your device:
https://riptutorial.com/ 935
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.Audio.AudioColumns.DATA,
MediaStore.Audio.AudioColumns.TITLE, MediaStore.Audio.AudioColumns.ALBUM,
MediaStore.Audio.ArtistColumns.ARTIST,};
Cursor c = context.getContentResolver().query(uri, projection, MediaStore.Audio.Media.DATA
+ " like ? ", new String[]{"%utm%"}, null);
if (c != null) {
while (c.moveToNext()) {
AudioModel audioModel = new AudioModel();
String path = c.getString(0);
String name = c.getString(1);
String album = c.getString(2);
String artist = c.getString(3);
audioModel.setaName(name);
audioModel.setaAlbum(album);
audioModel.setaArtist(artist);
audioModel.setaPath(path);
tempAudioList.add(audioModel);
}
c.close();
}
return tempAudioList;
}
The code above will return a list of all MP3 files with the music's name, path, artist, and album. For
more details please refer to the Media.Store.Audio documentation.
In order to read files of a specific folder, use the following query (you need to replace the folder
name):
Cursor c = context.getContentResolver().query(uri,
projection,
MediaStore.Audio.Media.DATA + " like ? ",
new String[]{"%yourFolderName%"}, // Put your device folder / file location here.
null);
If you want to retrieve all files from your device, then use the following query:
Cursor c = context.getContentResolver().query(uri,
projection,
null,
null,
null);
Now, all you have to do is to call the method above in order to get the MP3 files:
getAllAudioFromDevice(this);
https://riptutorial.com/ 936
Example with Activity
public class ReadAudioFilesActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_audio_list);
/**
* This will return a list of all MP3 files. Use the list to display data.
*/
getAllAudioFromDevice(this);
}
if (c != null) {
while (c.moveToNext()) {
// Create a model object.
AudioModel audioModel = new AudioModel();
https://riptutorial.com/ 937
Chapter 169: Memory Leaks
Examples
Common memory leaks and how to fix them
Make sure there is absolutely no static reference to View, Context or any of their descendants.
https://riptutorial.com/ 938
For example, I have an intentService that uses the Google location service API. And I forgot to call
googleApiClient.disconnect();:
A word of caution: AsyncTask has many gotcha's apart from the memory leak
described here. So be careful with this API, or avoid it altogether if you don't fully
understand the implications. There are many alternatives (Thread, EventBus,
RxAndroid, etc).
One common mistake with AsyncTask is to capture a strong reference to the host Activity (or
Fragment):
This is a problem because AsyncTask can easily outlive the parent Activity, for example if a
configuration change happens while the task is running.
The right way to do this is to make your task a static class, which does not capture the parent,
and holding a weak reference to the host Activity:
https://riptutorial.com/ 939
class MyActivity extends Activity {
static class MyTask extends AsyncTask<Void, Void, Void> {
// Weak references will still allow the Activity to be garbage-collected
private final WeakReference<MyActivity> weakActivity;
MyTask(MyActivity myActivity) {
this.weakActivity = new WeakReference<>(myActivity);
}
@Override
public Void doInBackground(Void... params) {
// do async stuff here
}
@Override
public void onPostExecute(Void result) {
// Re-acquire a strong reference to the activity, and verify
// that it still exists and is active.
MyActivity activity = weakActivity.get();
if (activity == null
|| activity.isFinishing()
|| activity.isDestroyed()) {
// activity is no longer valid, don't do anything!
return;
}
Every Time you create an anonymous class, it retains an implicit reference to its parent class. So
when you write:
...
foo.registerCallback(new BarCallback()
{
@Override
public void onBar()
{
// do something
}
});
}
You are in fact sending a reference to your LeakyActivity instance to foo. When the user navigates
away from your LeakyActivity, this reference can prevent the LeakyActivity instance from being
garbage collected. This is a serious leak as activities hold a reference to their entire view hierarchy
and are therefore rather large objects in memory.
https://riptutorial.com/ 940
You can of course avoid using anonymous callbacks in activities entirely. You can also unregister
all of your callbacks with respect to the activity lifecycle. like so:
@Override
protected void onResume()
{
super.onResume();
foo.registerCallback(mBarCallback);
}
@Override
protected void onPause()
{
super.onPause();
foo.unregisterCallback(mBarCallback);
}
}
Often you will want to wrap some of Android's classes in easier to use utility classes. Those utility
classes often require a context to access the android OS or your apps' resources. A common
example of this is a wrapper for the SharedPreferences class. In order to access Androids shared
preferences one must write:
context.getSharedPreferences(prefsName, mode);
https://riptutorial.com/ 941
now, if you call init() with your activity context, the LeakySharedPrefsWrapper will retain a
reference to your activity, preventing it from being garbage collected.
How to avoid:
When calling static helper functions, you can send in the application context using
context.getApplicationContext();
When creating static helper functions, you can extract the application context from the context you
are given (Calling getApplicationContext() on the application context returns the application
context). So the fix to our wrapper is simple:
If the application context is not appropriate for your use case, you can include a Context
parameter in each utility function, you should avoid keeping references to these context
parameters. In this case the solution would look like so:
LeakCanary is an Open Source Java library to detect memory leaks in your debug builds.
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
}
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
https://riptutorial.com/ 942
}
}
Now LeakCanary will automatically show a notification when an activity memory leak is detected in
your debug build.
NOTE: Release code will contain no reference to LeakCanary other than the two empty classes
that exist in the leakcanary-android-no-op dependency.
If you implement or create a listener in an Activity, always pay attention to the lifecycle of the
object that has the listener registered.
private UserController() {
// Init
}
https://riptutorial.com/ 943
Then there are two activities, SignInActivity:
UserController userController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.userController = UserController.getInstance();
this.userController.registerUserStateChangeListener(this);
}
@Override
public void userLoggedIn() {
startMainActivity();
}
@Override
public void userLoggedOut() {
showLoginForm();
}
...
And MainActivity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.userController = UserController.getInstance();
this.userController.registerUserStateChangeListener(this);
}
@Override
public void userLoggedIn() {
showUserAccount();
}
@Override
public void userLoggedOut() {
finish();
}
...
https://riptutorial.com/ 944
}
What happens with this example is that every time the user logs in and then logs out again, a
MainActivity instance is leaked. The leak occurs because there is a reference to the activity in
UserController#listeners.
Please note: Even if we use an anonymous inner class as a listener, the activity would still leak:
...
this.userController.registerUserStateChangeListener(new UserController.StateListener() {
@Override
public void userLoggedIn() {
showUserAccount();
}
@Override
public void userLoggedOut() {
finish();
}
});
...
The activity would still leak, because the anonymous inner class has an implicit reference to the
outer class (in this case the activity). This is why it is possible to call instance methods in the outer
class from the inner class. In fact, the only type of inner classes that do not have a reference to the
outer class are static inner classes.
In short, all instances of non-static inner classes hold an implicit reference to the instance of the
outer class that created them.
There are two main approaches to solving this, either by adding a method to remove a listener
from UserController#listeners or using a WeakReference to hold the reference of the listeners.
...
...
}
https://riptutorial.com/ 945
public class MainActivity extends Activity implements UserController.StateListener{
...
@Override
protected void onDestroy() {
super.onDestroy();
userController.removeUserStateChangeListener(this);
}
}
With this modification the instances of MainActivity are no longer leaked when the user logs in and
out. However, if the documentation isn't clear, chances are that the next developer that starts
using UserController might miss that it is required to unregister the listener when the activity is
destroyed, which leads us to the second method of avoiding these types of leaks.
First off, let us start by explaining what a weak reference is. A weak reference, as the name
suggests, holds a weak reference to an object. Compared to a normal instance field, which is a
strong reference, a weak references does not stop the garbage collector, GC, from removing the
objects. In the example above this would allow MainActivity to be garbage-collected after it has
been destroyed if the UserController used WeakReference to the reference the listeners.
In short, a weak reference is telling the GC that if no one else has a strong reference to this object,
go ahead and remove it.
Let us modify the UserController to use a list of WeakReference to keep track of it's listeners:
...
private List<WeakReference<StateListener>> listeners;
...
https://riptutorial.com/ 946
listener.userLoggedOut();
} else {
referencesToRemove.add(listenerRef);
}
}
}
With this modification it doesn't matter whether or not the listeners are removed, since
UserController holds no strong references to any of the listeners. However, writing this boilerplate
code every time is cumbersome. Therefore, let us create a generic class called WeakCollection:
public WeakCollection() {
this.list = new LinkedList<>();
}
public void put(T item){
//Make sure that we don't re add an item if we already have the reference.
List<T> currentList = get();
for(T oldItem : currentList){
if(item == oldItem){
return;
}
}
list.add(new WeakReference<T>(item));
}
https://riptutorial.com/ 947
public void remove(T listener) {
WeakReference<T> refToRemove = null;
for (WeakReference<T> ref : list) {
T item = ref.get();
if (item == listener) {
refToRemove = ref;
}
}
if(refToRemove != null){
list.remove(refToRemove);
}
}
}
...
}
As shown in the code example above, the WeakCollection<T> removes all of the boilerplate code
needed to use WeakReference instead of a normal list. To top it all off: If a call to
UserController#removeUserStateChangeListener(StateListener) is missed, the listener, and all the
objects it is referencing, will not leak.
Avoid memory leaks with Anonymous Class, Handler, Timer Task, Thread
In android, every developer uses Anonymous Class (Runnable) at least once in a project. Any
Anonymous Class has a reference to its parent (activity). If we perform a long-running task, the
parent activity will not be destroyed until the task is ended.
Example uses handler and Anonymous Runnable class. The memory will be leak when we quit the
https://riptutorial.com/ 948
activity before the Runnable is finished.
1. Dont do any long operating with Anonymous Class or we need a Static class for it and pass
WeakReference into it (such as activity, view...). Thread is the same with Anonymous Class.
2. Cancel the Handler, Timer when activity is destroyed.
https://riptutorial.com/ 949
Chapter 170: Menu
Syntax
• inflater.inflate(R.menu.your_xml_file, menu);
Parameters
Parameter Description
onCreateOptionsMenu (Menu Initialize the contents of the Activity's standard options menu.
menu) You should place your menu items in to menu.
Remarks
To know more about Menus, read this. Hope it helps!
Examples
Options menu with dividers
In Android there is a default options menu, which can take a number of options. If a larger number
of options needs to be displayed, then it makes sense to group those options in order to maintain
clarity. Options can be grouped by putting dividers (i.e. horizontal lines) between them. In order to
allow for dividers, the following theme can be used:
https://riptutorial.com/ 950
By changing the theme, dividers can be added to a menu.
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
applyFontToMenu(menu,this);
return true;
}
To define your own menu, create an XML file inside your project's res/menu/ directory and build the
menu with the following elements:
Step 1:
Create your own xml file as the following:
In res/menu/main_menu.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/aboutMenu"
https://riptutorial.com/ 951
android:title="About" />
<item
android:id="@+id/helpMenu"
android:title="Help" />
<item
android:id="@+id/signOutMenu"
android:title="Sign Out" />
</menu>
Step 2:
To specify the options menu, override onCreateOptionsMenu() in your activity.
In this method, you can inflate your menu resource (defined in your XML file i.e.,
res/menu/main_menu.xml)
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
return true;
}
When the user selects an item from the options menu, the system calls your activity's overridden
onOptionsItemSelected() method.
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.aboutMenu:
Log.d(TAG, "Clicked on About!");
// Code for About goes here
return true;
case R.id.helpMenu:
Log.d(TAG, "Clicked on Help!");
// Code for Help goes here
return true;
case R.id.signOutMenu:
Log.d(TAG, "Clicked on Sign Out!");
// SignOut method call goes here
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Wrapping up!
https://riptutorial.com/ 952
Your Activity code should look like below:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.aboutMenu:
Log.d(TAG, "Clicked on About!");
// Code for About goes here
return true;
case R.id.helpMenu:
Log.d(TAG, "Clicked on Help!");
// Code for Help goes here
return true;
case R.id.signOutMenu:
Log.d(TAG, "User signed out");
// SignOut method call goes here
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
https://riptutorial.com/ 953
Read Menu online: https://riptutorial.com/android/topic/2028/menu
https://riptutorial.com/ 954
Chapter 171: Moshi
Introduction
Moshi is a modern JSON library for Android and Java. It makes it easy to parse JSON into Java
objects and Java back into JSON.
Remarks
Don't forget, always read the README!
Examples
JSON into Java
Moshi has built-in support for reading and writing Java’s core data types:
• Primitives (int, float, char...) and their boxed counterparts (Integer, Float, Character...).
• Arrays
• Collections
• Lists
• Sets
• Maps Strings Enums
It supports your model classes by writing them out field-by-field. In the example above Moshi uses
https://riptutorial.com/ 955
these classes:
class BlackjackHand {
public final Card hidden_card;
public final List<Card> visible_cards;
...
}
class Card {
public final char rank;
public final Suit suit;
...
}
enum Suit {
CLUBS, DIAMONDS, HEARTS, SPADES;
}
to read and write this JSON:
{
"hidden_card": {
"rank": "6",
"suit": "SPADES"
},
"visible_cards": [
{
"rank": "4",
"suit": "CLUBS"
},
{
"rank": "A",
"suit": "HEARTS"
}
]
}
https://riptutorial.com/ 956
Chapter 172: Multidex and the Dex Method
Limit
Introduction
DEX means Android app's (APK) executable bytecode files in the form of Dalvik Executable (DEX)
files, which contain the compiled code used to run your app.
The Dalvik Executable specification limits the total number of methods that can be referenced
within a single DEX file to 65,536 (64K)—including Android framework methods, library methods,
and methods in your own code.
To overcome this limit requires configure your app build process to generate more than one DEX
file, known as a Multidex.
Remarks
What is dex?
Dex is the name of the file format and encoding to which Android Java code is compiled. Early
versions of Android would load and execute dex binaries directly in a virtual machine named
Dalvik. More recent versions of Android use the Android Runtime (ART), which treats dex files as
an intermediate representation and performs further compilations on it prior to running the
application.
Dex is a very old file format, in terms of the lifespan of smartphones, and was designed for devices
whose main memory was measured in tens of megabytes. The design limitations of those days
have remained with us to this day.
The problem:
The dex file format encodes a limit to the number of methods that can be referenced in a single
binary. Because the portion of the file format that stores the number of references is two bytes
long, the maximum number of method references is 0xFFFF, or 65535. If an application contains
more than that number of method references, it will fail to compile.
This approach works well on newer devices, but has some substantial drawbacks. It can increase
https://riptutorial.com/ 957
application startup time dramatically, and on older devices can cause Application Not Responding
failures.
• Review your app's direct and transitive dependencies - Ensure any large library
dependency you include in your app is used in a manner that outweighs the amount of code
being added to the application. A common anti-pattern is to include a very large library
because a few utility methods were useful. Reducing your app code dependencies can often
help you avoid the dex reference limit.
• Remove unused code with ProGuard - Configure the ProGuard settings for your app to
run ProGuard and ensure you have shrinking enabled for release builds. Enabling shrinking
ensures you are not shipping unused code with your APKs.
The first point requires diligence and discipline on the part of the developer. When incorporating
third-party libraries, one must consider the size of the library. For example, two popular JSON
libraries are Jackson and Gson. Functionally they are quite similar, but Gson tends to see greater
use in Android. One reason is that Jackson weighs in around 9,000 methods, whereas Gson
contributes 1,900.
There are several tools available to help developers keep track of the size of their application:
Examples
Multidex by using MultiDexApplication directly
This is the simplest option, but this way you can't provide your own Application subclass. If an
Application subclass is needed, you will have to switch to one of the other options to do so.
https://riptutorial.com/ 958
the AndroidManifest.xml:
Specify this Application subclass using the android:name property in the manifest file inside the
application tag.
In the Application subclass, add the attachBaseContext() method override, and in that method call
MultiDex.install():
package com.example;
import android.app.Application;
import android.content.Context;
/**
* Extended application that support multidex
*/
public class MyApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
Ensure that the Application subclass is specified in the application tag of your
AndroidManifest.xml:
<application
android:name="com.example.MyApplication"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
</application>
Enabling Multidex
https://riptutorial.com/ 959
• to use a MultiDexApplication or enable the MultiDex in your Application class
Gradle configuration
In app/build.gradle add these parts:
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
defaultConfig {
...
minSdkVersion 14
targetSdkVersion 24
...
dependencies {
compile 'com.android.support:multidex:1.0.1'
}
When these configuration settings are added to an app, the Android build tools construct a primary
dex (classes.dex) and supporting (classes2.dex, classes3.dex) as needed.
The build system will then package them into an APK file for distribution.
The dexcount plugin counts methods and class resource count after a successful build.
buildscript {
repositories {
mavenCentral() // or jcenter()
}
https://riptutorial.com/ 960
dependencies {
classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.5.5'
}
}
../app/build/outputs/dexcount
../app/build/outputs/dexcount/debugChart/index.html
This is very similar to using an Application subclass and overriding the attachBaseContext()
method.
However, using this method, you don't need to override attachBaseContext() as this is already done
in the MultiDexApplication superclass.
package com.example;
import android.support.multidex.MultiDexApplication;
import android.content.Context;
/**
* Extended MultiDexApplication
*/
public class MyApplication extends MultiDexApplication {
//..........
}
Add this class to your AndroidManifest.xml exactly as if you were extending Application:
<application
android:name="com.example.MyApplication"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
</application>
https://riptutorial.com/ 961
Chapter 173: MVP Architecture
Introduction
This topic will provide Model‑View‑Presenter (MVP) architecture of Android with various examples.
Remarks
There are many ways to architect an Android app. But not all of them are testable and allows us to
structure our code so that the app is easy to test. The key idea of a testable architecture is
separating parts of the application which makes them easier to maintain, extend and test
separately from each other.
MVP Definition
Model
In an application with a good layered architecture, this model would only be the gateway to the
domain layer or business logic. See it as the provider of the data we want to display in the view.
View
The View, usually implemented by an Activity or Fragment, will contain a reference to the presenter
. The only thing that the view will do is to call a method from the Presenter every time there is an
interface action.
Presenter
The Presenter is responsible to act as the middle man between View and Model. It retrieves data
from the Model and returns it formatted to the View. But unlike the typical MVC, it also decides
what happens when you interact with the View.
Examples
Login example in the Model View Presenter (MVP) pattern
https://riptutorial.com/ 962
Let's see MVP in action using a simple Login Screen. There are two Buttons—one for login action
and another for a registration screen; two EditTexts—one for the email and the other for the
password.
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState) {
return inflater.inflate(R.layout.fragment_login, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
email = (EditText) view.findViewById(R.id.email_et);
password = (EditText) view.findViewById(R.id.password_et);
login = (Button) view.findViewById(R.id.login_btn);
login.setOnClickListener(this);
register = (Button) view.findViewById(R.id.register_btn);
register.setOnClickListener(this);
presenter.isLoggedIn();
@Override
public void onLoginResponse(boolean isLoginSuccess) {
if (isLoginSuccess) {
startActivity(new Intent(getActivity(), MapActivity.class));
getActivity().finish();
}
}
@Override
public void onError(String message) {
Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
}
@Override
public void isLoggedIn(boolean isLoggedIn) {
if (isLoggedIn) {
startActivity(new Intent(getActivity(), MapActivity.class));
getActivity().finish();
}
}
@Override
public void onClick(View view) {
https://riptutorial.com/ 963
switch (view.getId()) {
case R.id.login_btn:
LoginItem loginItem = new LoginItem();
loginItem.setPassword(password.getText().toString().trim());
loginItem.setEmail(email.getText().toString().trim());
presenter.login(loginItem);
break;
case R.id.register_btn:
startActivity(new Intent(getActivity(), RegisterActivity.class));
getActivity().finish();
break;
}
}
}
@Override
public void login(LoginItem userCredentials) {
model.login(userCredentials);
}
@Override
public void isLoggedIn() {
model.isLoggedIn();
}
@Override
public void onLoginResponse(boolean isLoginSuccess) {
view.onLoginResponse(isLoginSuccess);
}
@Override
public void onError(String message) {
view.onError(message);
}
@Override
public void isloggedIn(boolean isLoggedin) {
view.isLoggedIn(isLoggedin);
}
}
https://riptutorial.com/ 964
private LoginContract.ToPresenter presenter;
@Override
public void login(LoginItem userCredentials) {
if (validateData(userCredentials)) {
try {
performLoginOperation(userCredentials);
} catch (JSONException e) {
e.printStackTrace();
}
} else {
presenter.onError(BaseContext.getContext().getString(R.string.error_login_field_validation));
}
}
@Override
public void isLoggedIn() {
DatabaseHelper database = new DatabaseHelper(BaseContext.getContext());
presenter.isloggedIn(database.isLoggedIn());
}
@Override
public void onError(String message) {
https://riptutorial.com/ 965
presenter.onError(message);
}
}
Class Diagram
Let's see the action in the form of class diagram.
Notes:
https://riptutorial.com/ 966
• This example uses Volley for network communication, but this library is not required for MVP
• UrlUtils is a class which contains all the links for my API Endpoints
• ResponseErrorListener.ErrorListener is an interface that listens for error in ErrorResponse that
implements Volley's Response.ErrorListener; these classes are not included here as they are
not directly part of this example
XML activity_login
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
https://riptutorial.com/ 967
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<EditText
android:id="@+id/et_login_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="USERNAME" />
<EditText
android:id="@+id/et_login_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="PASSWORD" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_login_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="4dp"
android:layout_weight="1"
android:text="Login" />
<Button
android:id="@+id/btn_login_clear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_weight="1"
android:text="Clear" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="correct user: mvp, mvp" />
<ProgressBar
android:id="@+id/progress_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dp" />
</LinearLayout>
https://riptutorial.com/ 968
private Button btnClear;
private ILoginPresenter loginPresenter;
private ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//find view
editUser = (EditText) this.findViewById(R.id.et_login_username);
editPass = (EditText) this.findViewById(R.id.et_login_password);
btnLogin = (Button) this.findViewById(R.id.btn_login_login);
btnClear = (Button) this.findViewById(R.id.btn_login_clear);
progressBar = (ProgressBar) this.findViewById(R.id.progress_login);
//set listener
btnLogin.setOnClickListener(this);
btnClear.setOnClickListener(this);
//init
loginPresenter = new LoginPresenterCompl(this);
loginPresenter.setProgressBarVisiblity(View.INVISIBLE);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_login_clear:
loginPresenter.clear();
break;
case R.id.btn_login_login:
loginPresenter.setProgressBarVisiblity(View.VISIBLE);
btnLogin.setEnabled(false);
btnClear.setEnabled(false);
loginPresenter.doLogin(editUser.getText().toString(),
editPass.getText().toString());
break;
}
}
@Override
public void onClearText() {
editUser.setText("");
editPass.setText("");
}
@Override
public void onLoginResult(Boolean result, int code) {
loginPresenter.setProgressBarVisiblity(View.INVISIBLE);
btnLogin.setEnabled(true);
btnClear.setEnabled(true);
if (result){
Toast.makeText(this,"Login Success",Toast.LENGTH_SHORT).show();
}
else
Toast.makeText(this,"Login Fail, code = " + code,Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
https://riptutorial.com/ 969
super.onDestroy();
}
@Override
public void onSetProgressBarVisibility(int visibility) {
progressBar.setVisibility(visibility);
}
}
ILoginPresenter.class
LoginPresenterCompl.class
@Override
public void clear() {
iLoginView.onClearText();
https://riptutorial.com/ 970
}
@Override
public void doLogin(String name, String passwd) {
Boolean isLoginSuccess = true;
final int code = user.checkUserValidity(name,passwd);
if (code!=0) isLoginSuccess = false;
final Boolean result = isLoginSuccess;
handler.postDelayed(new Runnable() {
@Override
public void run() {
iLoginView.onLoginResult(result, code);
}
}, 5000);
}
@Override
public void setProgressBarVisiblity(int visiblity){
iLoginView.onSetProgressBarVisibility(visiblity);
}
Creating a UserModel
Create a UserModel which is like a Pojo class for LoginActivity. Create an IUser interface for Pojo
validations:
UserModel.class
@Override
public String getName() {
return name;
}
@Override
public String getPasswd() {
return passwd;
}
@Override
public int checkUserValidity(String name, String passwd){
if (name==null||passwd==null||!name.equals(getName())||!passwd.equals(getPasswd())){
return -1;
https://riptutorial.com/ 971
}
return 0;
}
IUser.class
String getPasswd();
MVP
A Model-view-presenter (MVP) is a derivation of the model–view–controller (MVC) architectural
pattern. It is used mostly for building user interfaces and offers the following benefits:
• Views are more separated from Models. The Presenter is the mediator between Model and
View.
• It is easier to create unit tests.
• Generally, there is a one-to-one mapping between View and Presenter, with the possibility to
use multiple Presenters for complex Views.
https://riptutorial.com/ 972
Read MVP Architecture online: https://riptutorial.com/android/topic/4615/mvp-architecture
https://riptutorial.com/ 973
Chapter 174: MVVM (Architecture)
Remarks
Syntax quirks with DataBinding
When binding a viewModel function to a property in xml certain function prefixes like get or is are
dropped. Eg. ViewModel::getFormattedText on the ViewModel will become
@{viewModel.formattedText} when binding it to a property in xml. Similarly with
ViewModel::isContentVisible -> @{viewModel.contentVisible} (Java Bean notation)
The generated binding classes like ActivityMainBinding are named after the xml they're creating
bindings for, not the java class.
Custom Bindings
In the activity_main.xml I set the textColor attribute on the app and not the android namespace.
Why is that? Because there is a custom setter defined for the attribute textColor that resolves a
ColorRes resource id send out by the ViewModel to an actual color.
@TargetApi(23)
@BindingAdapter({"bind:textColor"})
public static void setTextColor(TextView textView, int colorResId) {
final Context context = textView.getContext();
final Resources resources = context.getResources();
final int apiVersion = Build.VERSION.SDK_INT;
int color;
textView.setTextColor(color);
}
For details on how this works check DataBinding Library: Custom Setters
You could argue that the things I do in xml for android:visibility and app:textColor are wrong/anti-
patterns in the MVVM context because there is view logic in my view. However I would argue it is
more important for me to keep android dependencies out of my ViewModel for testing reasons.
Besides, what really does app:textColor do? It only resolves a ressource pointer to the actual color
associated with it. So the ViewModel still decides which color is shown based on some condition.
https://riptutorial.com/ 974
As for the android:visibility I feel because of how the method is named it is actually okay to use
the ternary operator here. Because of the name isLoadingVisible and isContentVisible there is
really no doubt about what each outcome should resolve to in the view. So I feel it is rather
executing a command given by the ViewModel than really doing view logic.
On the other hand I would agree that using viewModel.isLoading ? View.VISIBLE : View.GONE would
be a bad thing to do because you are making assumptions in the view what that state means for
the view.
Useful Material
The following resources have helped me a lot in trying to understand this concept:
Examples
MVVM Example using DataBinding Library
The whole point of MVVM is to separate layers containing logic from the view layer.
On Android we can use the DataBinding Library to help us with this and make most of our logic
Unit-testable without worrying about Android dependencies.
In this example I'll show the central components for a stupid simple App that does the following:
activity_main.xml:
If you're unfamiliar with how DataBinding works you should probably take 10 minutes to make
yourself familiar with it. As you can see, all fields you would usually update with setters are bound
to functions on the viewModel variable.
If you've got a question about the android:visibility or app:textColor properties check the
'Remarks' section.
<layout
https://riptutorial.com/ 975
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="de.walled.mvvmtest.viewmodel.ClickerViewModel"/>
</data>
<RelativeLayout
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/activity_horizontal_margin"
tools:context="de.walled.mvvmtest.view.MainActivity">
<LinearLayout
android:id="@+id/click_counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="60dp"
android:visibility="@{viewModel.contentVisible ? View.VISIBLE : View.GONE}"
android:padding="8dp"
android:orientation="horizontal">
<TextView
android:id="@+id/number_of_clicks"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/ClickCounter"
android:text="@{viewModel.numberOfClicks}"
android:textAlignment="center"
app:textColor="@{viewModel.counterColor}"
tools:text="8"
tools:textColor="@color/red"
/>
<TextView
android:id="@+id/static_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
style="@style/ClickCounter"
android:text="@string/label.clicks"
app:textColor="@{viewModel.counterColor}"
android:textAlignment="center"
tools:textColor="@color/red"
/>
https://riptutorial.com/ 976
</LinearLayout>
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/click_counter"
android:layout_centerHorizontal="true"
android:visibility="@{viewModel.contentVisible ? View.VISIBLE : View.GONE}"
android:text="@{viewModel.labelText}"
android:textAlignment="center"
android:textSize="18sp"
<Button
android:id="@+id/clicker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/message"
android:layout_centerHorizontal="true"
android:layout_marginTop="8dp"
android:visibility="@{viewModel.contentVisible ? View.VISIBLE : View.GONE}"
android:padding="8dp"
android:text="@string/label.button"
<android.support.v4.widget.ContentLoadingProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="90dp"
android:layout_centerHorizontal="true"
style="@android:style/Widget.ProgressBar.Inverse"
android:visibility="@{viewModel.loadingVisible ? View.VISIBLE : View.GONE}"
android:indeterminate="true"
/>
</RelativeLayout>
</layout>
Also I define here a 'state of excitement' that is dependent on the number of clicks. This will later
be used to update color and message on the View.
https://riptutorial.com/ 977
It is important to note that there are no assumptions made in the model about how the state might
be displayed to the user!
ClickerModel.java
import com.google.common.base.Optional;
import de.walled.mvvmtest.viewmodel.ViewState;
This will trigger changes on the model and format data from the model to show them on the view.
Note that it is here where we evaluate which GUI representation is appropriate for the state given
by the model (resolveCounterColor and resolveLabelText). So we could for example easily
implement an UnderachieverClickerModel that has lower thresholds for the state of excitement
without touching any code in the viewModel or view.
Also note that the ViewModel does not hold any references to view objects. All properties are
bound via the @Bindable annotations and updated when either notifyChange() (signals all properties
need to be updated) or notifyPropertyChanged(BR.propertyName) (signals this properties need to be
updated).
ClickerViewModel.java
https://riptutorial.com/ 978
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.support.annotation.ColorRes;
import android.support.annotation.StringRes;
import com.android.databinding.library.baseAdapters.BR;
import de.walled.mvvmtest.R;
import de.walled.mvvmtest.api.IClickerApi;
import de.walled.mvvmtest.model.Excitement;
import de.walled.mvvmtest.model.IClickerModel;
import rx.Observable;
@Bindable
public String getNumberOfClicks() {
final int clicks = model.getNumberOfClicks();
return String.valueOf(clicks);
}
@Bindable
@StringRes
public int getLabelText() {
final Excitement stateOfExcitement = model.getStateOfExcitement();
https://riptutorial.com/ 979
return resolveLabelText(stateOfExcitement);
}
@Bindable
@ColorRes
public int getCounterColor() {
final Excitement stateOfExcitement = model.getStateOfExcitement();
return resolveCounterColor(stateOfExcitement);
}
@Bindable
public boolean isLoadingVisible() {
return isLoading;
}
@Bindable
public boolean isContentVisible() {
return !isLoading;
}
@ColorRes
private int resolveCounterColor(Excitement stateOfExcitement) {
switch (stateOfExcitement) {
case MEH:
return R.color.yellow;
case WOOHOO:
return R.color.green;
default:
return R.color.red;
}
}
@StringRes
private int resolveLabelText(Excitement stateOfExcitement) {
switch (stateOfExcitement) {
case MEH:
return R.string.label_indifferent;
case WOOHOO:
return R.string.label_excited;
default:
return R.string.label_negative;
}
}
Here we see the view initializing the viewModel with all dependencies it might need, that have to
be instantiated from an android context.
After the viewModel is initialized it is bound to the xml layout via the DataBindingUtil (Please check
'Syntax' section for naming of generated classes).
https://riptutorial.com/ 980
Note subscriptions are subscribed to on this layer because we have to handle unsubscribing them
when the activity is paused or destroyed to avoid memory leaks and NPEs. Also persisting and
reloading of the viewState on OrientationChanges is triggered here
MainActivity.java
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import de.walled.mvvmtest.R;
import de.walled.mvvmtest.api.ClickerApi;
import de.walled.mvvmtest.api.IClickerApi;
import de.walled.mvvmtest.databinding.ActivityMainBinding;
import de.walled.mvvmtest.model.ClickerModel;
import de.walled.mvvmtest.viewmodel.ClickerViewModel;
import de.walled.mvvmtest.viewmodel.ViewState;
import rx.Subscription;
import rx.subscriptions.Subscriptions;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@Override
protected void onPause() {
fakeLoader.unsubscribe();
super.onPause();
}
@Override
protected void onDestroy() {
fakeLoader.unsubscribe();
super.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putSerializable(KEY_VIEW_STATE, viewModel.getViewState());
}
https://riptutorial.com/ 981
if (savedState == null) {
fakeLoader = viewModel.loadData().subscribe();
} else {
viewModel.initFromSavedState(savedState);
}
}
https://riptutorial.com/ 982
Chapter 175: NavigationView
Remarks
The NavigationView represents a standard navigation menu for application. The menu contents
can be populated by a menu resource file.
Before using the NavigationView you must add the design support library dependency in the
build.gradle file:
dependencies {
compile 'com.android.support:design:24.2.0'
}
Official Documentation:
https://developer.android.com/reference/android/support/design/widget/NavigationView.html
Examples
How to add the NavigationView
To use a NavigationView just add the dependency in the build.gradle file as described in the
remarks section
<include
layout="@layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.NavigationView
https://riptutorial.com/ 983
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer" />
</android.support.v4.widget.DrawerLayout>
res/layout/nav_header_main.xml: The view which will been displayed on the top of the drawer
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:src="@android:drawable/sym_def_app_icon"
android:id="@+id/imageView" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:text="Android Studio"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="[email protected]"
android:id="@+id/textView" />
</LinearLayout>
res/layout/app_bar_main.xml An abstraction layer for the toolbar to separate it from the content:
<android.support.design.widget.AppBarLayout
https://riptutorial.com/ 984
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
res/layout/content_main.xml The real content of the activity just for demo, here you would put your
normal layout xml:
<TextView
android:text="Hello World!"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<group android:checkableBehavior="single">
<item
android:id="@+id/nav_camera"
android:icon="@drawable/ic_menu_camera"
https://riptutorial.com/ 985
android:title="Import" />
<item
android:id="@+id/nav_gallery"
android:icon="@drawable/ic_menu_gallery"
android:title="Gallery" />
<item
android:id="@+id/nav_slideshow"
android:icon="@drawable/ic_menu_slideshow"
android:title="Slideshow" />
<item
android:id="@+id/nav_manage"
android:icon="@drawable/ic_menu_manage"
android:title="Tools" />
</group>
<item android:title="Communicate">
<menu>
<item
android:id="@+id/nav_share"
android:icon="@drawable/ic_menu_share"
android:title="Share" />
<item
android:id="@+id/nav_send"
android:icon="@drawable/ic_menu_send"
android:title="Send" />
</menu>
</item>
</menu>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
https://riptutorial.com/ 986
}
@Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
switch(item.getItemId()) {/*...*/}
https://riptutorial.com/ 987
Add underline in menu elements
Each group ends with a line separator. If each item in your menu has its own group you will
achieve the desired graphical output. It will work only if your different groups have different
android:id. Also, in menu.xml remember to mention android:checkable="true" for single item and
android:checkableBehavior="single" for a group of items.
<item
android:id="@+id/pos_item_help"
android:checkable="true"
android:title="Help" />
<item
android:id="@+id/pos_item_pos"
android:checkable="true"
android:title="POS" />
<item
android:id="@+id/pos_item_orders"
android:checkable="true"
android:title="Orders" />
<group
android:id="@+id/group"
android:checkableBehavior="single">
<item
android:id="@+id/menu_nav_home"
android:icon="@drawable/ic_home_black_24dp"
android:title="@string/menu_nav_home" />
https://riptutorial.com/ 988
</group>
......
</menu>
https://riptutorial.com/ 989
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
Preview:
https://riptutorial.com/ 990
DividerItemDecoration(context,DividerItemDecoration.VERTICAL));
Preview :
https://riptutorial.com/ 991
Chapter 176: Notification Channel Android O
Introduction
Notification channels enable us app developers to group our notifications into
groups—channels—with the user having the ability to modify notification settings for the entire
channel at once.In Android O this feature is introduced.Right now it is available developers
preview.
Syntax
1. class NotificationUtils{} //For create notification channel
2. createChannel()//Generic method for creating notification
Parameters
Method Description
IMPORTANCE_MAX unused
IMPORTANCE_DEFAULT shows everywhere, makes noise, but does not visually intrude
Examples
Notification Channel
https://riptutorial.com/ 992
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Color;
getManager().createNotificationChannel(androidChannel);
In the code above, we created two instances of the NotificationChannel, passing uniqueid a
channel name, and also an importance level in its constructor. For each notification channel, we
applied following characteristics.
https://riptutorial.com/ 993
1. Sound
2. Lights
3. Vibration
4. Notification to show on lock screen.
Finally, we got the NotificationManager from the system and then registered the channel by calling
the method createNotificationChannel(), passing the channel we have created.
We can create multiple notification channels all at once with createNotificationChannels(), passing
a Java list of NotificationChannel instances. You can get all notification channels for an app with
getNotificationChannels() and get a specific channel with getNotificationChannel(), passing only
the channel id as an argument.
Method Description
IMPORTANCE_MAX unused
IMPORTANCE_DEFAULT shows everywhere, makes noise, but does not visually intrude
We have created two notification one using NotificationUtils another using NotificationBuilder.
https://riptutorial.com/ 994
Update Notification Channel Settings
Once you create a notification channel, the user is in charge of its settings and behavior. You can
call createNotificationChannel() again to rename an existing notification channel, or update its
description. The following sample code describes how you can redirect a user to the settings for a
notification channel by creating an intent to start an activity. In this case, the intent requires
extended data including the ID of the notification channel and the package name of your app.
@Override
protected void onCreate(Bundle savedInstanceState) {
//...
Button buttonAndroidNotifSettings = (Button)
findViewById(R.id.btn_android_notif_settings);
buttonAndroidNotifSettings.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent i = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
i.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
i.putExtra(Settings.EXTRA_CHANNEL_ID, NotificationUtils.ANDROID_CHANNEL_ID);
startActivity(i);
}
});
}
XML file:
<!--...-->
<Button
android:id="@+id/btn_android_notif_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Notification Settings"/>
<!--...-->
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// The id of the channel.
String id = "my_channel_01";
NotificationChannel mChannel = mNotificationManager.getNotificationChannel(id);
mNotificationManager.deleteNotificationChannel(mChannel);
activity_main.xml
https://riptutorial.com/ 995
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="16dp"
tools:context="com.chikeandroid.tutsplusalerts.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tuts+ Android Channel"
android:layout_gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.AppCompat.Title"/>
<EditText
android:id="@+id/et_android_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Title"/>
<EditText
android:id="@+id/et_android_author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Author"/>
<Button
android:id="@+id/btn_send_android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Send"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tuts+ IOS Channel"
android:layout_gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.AppCompat.Title"/>
<EditText
android:id="@+id/et_ios_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Title"
/>
<EditText
android:id="@+id/et_ios_author"
https://riptutorial.com/ 996
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Author"/>
<Button
android:id="@+id/btn_send_ios"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Send"/>
</LinearLayout>
</LinearLayout>
MainActivity.java
we are going to edit our MainActivity so that we can get the title and author from the EditText
components and then send these to the Android channel. We get the Notification.Builder for the
Android channel we created in our NotificationUtils, and then notify the NotificationManager.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonAndroid.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String title = editTextTitleAndroid.getText().toString();
String author = editTextAuthorAndroid.getText().toString();
mNotificationUtils.getManager().notify(107, nb.build());
}
}
});
}
}
https://riptutorial.com/ 997
Chapter 177: Notifications
Examples
Creating a simple Notification
This example shows how to create a simple notification that starts an application when the user
clicks it.
NotificationManager mNotificationManager =
(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(0, mBuilder.build());
Here is how to make a Heads Up Notification for capable devices, and use a Ticker for older
devices.
i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent notificationIntent = PendingIntent.getActivity(this, 999, i,
PendingIntent.FLAG_UPDATE_CURRENT);
https://riptutorial.com/ 998
NotificationCompat.Builder builder = new
NotificationCompat.Builder(this.getApplicationContext());
builder.setContentIntent(notificationIntent);
builder.setAutoCancel(true);
builder.setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
android.R.drawable.ic_menu_view));
builder.setSmallIcon(android.R.drawable.ic_dialog_map);
builder.setContentText("Test Message Text");
builder.setTicker("Test Ticker Text");
builder.setContentTitle("Test Message Title");
NotificationManager mNotificationManager =
(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(999, builder.build());
https://riptutorial.com/ 999
Here is what it looks like on Android KitKat with the Ticker:
https://riptutorial.com/ 1000
Android 4.4.x KitKat:
https://riptutorial.com/ 1001
Setting Different priorities in notification
NotificationCompat.Builder mBuilder =
.setSmallIcon(R.drawable.some_small_icon)
.setContentTitle("Title")
.setContentText("This is a test notification with MAX priority")
.setPriority(Notification.PRIORITY_MAX);
When notification contains image and you want to auto expand image when notification
received use "PRIORITY_MAX", you can use other priority levels as per requirments
PRIORITY_MAX -- Use for critical and urgent notifications that alert the user to a condition that is
time-critical or needs to be resolved before they can continue with a particular task.
PRIORITY_HIGH -- Use primarily for important communication, such as message or chat events
with content that is particularly interesting for the user. High-priority notifications trigger the heads-
up notification display.
PRIORITY_DEFAULT -- Use for all notifications that don't fall into any of the other priorities
https://riptutorial.com/ 1002
described here.
PRIORITY_LOW -- Use for notifications that you want the user to be informed about, but that are
less urgent. Low-priority notifications tend to show up at the bottom of the list, which makes them a
good choice for things like public or undirected social updates: The user has asked to be notified
about them, but these notifications should never take precedence over urgent or direct
communication.
Scheduling notifications
Sometimes it is required to display a notification at a specific time, a task that unfortunately is not
trivial on the Android system, as there is no method setTime() or similiar for notifications. This
example outlines the steps needed to schedule notifications using the AlarmManager:
This is the place where you build your notification based on the extras provided with the Intent:
2. Register the BroadcastReceiver in your AndroidManifest.xml file (otherwise the receiver won't
receive any Intents from the AlarmManager):
<receiver
android:name=".NotificationReceiver"
android:enabled="true" />
https://riptutorial.com/ 1003
public static void scheduleNotification(Context context, long time, String title, String
text) {
Intent intent = new Intent(context, NotificationReceiver.class);
intent.putExtra("title", title);
intent.putExtra("text", text);
PendingIntent pending = PendingIntent.getBroadcast(context, 42, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// Schdedule notification
AlarmManager manager = (AlarmManager)
context.getSystemService(Context.ALARM_SERVICE);
manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, pending);
}
Please note that the 42 above needs to be unique for each scheduled notification, otherwise
the PendingIntents will replace each other causing undesired effects!
Note that the 42 above needs to match the number from step 3!
If you want have a long text to display in the context, you need to set a custom content.
https://riptutorial.com/ 1004
All you need to do, is to add a style to your content like below:
builder.setContentTitle("Title").setContentText(message)
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setLargeIcon(largeIcon)
.setAutoCancel(true)
.setWhen(System.currentTimeMillis())
.setStyle(new NotificationCompat.BigTextStyle().bigText(message));
https://riptutorial.com/ 1005
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(uniqueIntentId, notification);
<ImageView
android:id="@+id/remoteview_notification_icon"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginRight="2dp"
android:layout_weight="0"
android:scaleType="centerCrop"/>
</LinearLayout>
Dynamically getting the correct pixel size for the large icon
If you're creating an image, decoding an image, or resizing an image to fit the large notification
image area, you can get the correct pixel dimensions like so:
https://riptutorial.com/ 1006
// Notification builder
builder = new NotificationCompat.Builder(context);
builder.setContentText("Ongoing Notification..");
builder.setContentTitle("ongoing notification sample");
builder.setSmallIcon(R.drawable.notification_icon);
builder.setUsesChronometer(true);
builder.setDefaults(Notification.DEFAULT_LIGHTS);
builder.setContentIntent(contentIntent);
builder.setOngoing(true);
Register a broadcast receiver for the same action to handle action button click event.
https://riptutorial.com/ 1007
Chapter 178: OkHttp
Examples
Logging interceptor
Interceptors are used to intercept OkHttp calls. The reason to intercept could be to monitor, rewrite
and retry calls. It can be used for outgoing request or incoming response both.
long t1 = System.nanoTime();
logger.info(String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
long t2 = System.nanoTime();
logger.info(String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
return response;
}
}
Rewriting Responses
I like to wrap my OkHttp into a class called HttpClient for example, and in this class I have methods
for each of the major HTTP verbs, post, get, put and delete, most commonly. (I usually include an
interface, in order to keep for it to implement, in order to be able to easily change to a different
implementation, if need be):
https://riptutorial.com/ 1008
OkHttpClient httpClient = new OkHttpClient();
@Override
public String post(String url, String json) throws IOException {
Log.i(TAG, "Sending a post request with body:\n" + json + "\n to URL: " + url);
The syntax is the same for put, get and delete except for 1 word (.put(body)) so it might be
obnoxious to post that code as well. Usage is pretty simple, just call the appropriate method on
some url with some json payload and the method will return a string as a result that you can later
use and parse. Let's assume that the response will be a json, we can create a JSONObject easily
from it:
System.out.println(response.body().string());
}
client.newCall(request).enqueue(new Callback() {
@Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
https://riptutorial.com/ 1009
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
});
}
System.out.println(response.body().string());
}
System.out.println(response.body().string());
https://riptutorial.com/ 1010
}
Setting up OkHttp
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.6.0</version>
</dependency>
or Gradle:
compile 'com.squareup.okhttp3:okhttp:3.6.0'
https://riptutorial.com/ 1011
Chapter 179: Okio
Examples
Download / Implement
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>1.12.0</version>
</dependency>
or Gradle:
compile 'com.squareup.okio:okio:1.12.0'
PNG decoder
while (true) {
Buffer chunk = new Buffer();
decodeChunk(type, chunk);
if (type.equals("IEND")) break;
}
}
}
https://riptutorial.com/ 1012
}
}
Okio is built around two types that pack a lot of capability into a straightforward API:
Buffer is a mutable sequence of bytes. Like ArrayList, you don't need to size your buffer in
advance. You read and write buffers as a queue: write data to the end and read it from the front.
There's no obligation to manage positions, limits, or capacities.
Internally, ByteString and Buffer do some clever things to save CPU and memory. If you encode a
UTF-8 string as a ByteString, it caches a reference to that string so that if you decode it later,
there's no work to do.
Bufferis implemented as a linked list of segments. When you move data from one buffer to
another, it reassigns ownership of the segments rather than copying the data across. This
approach is particularly helpful for multithreaded programs: a thread that talks to the network can
exchange data with a worker thread without any copying or ceremony.
https://riptutorial.com/ 1013
Chapter 180: Optimized VideoView
Introduction
Playing a video using a VideoView which extends SurfaceView inside of a row of a ListView seems to
work at first, until the user tries to scroll the list. As soon as the list starts to scroll, the video turns
black (sometimes displays white). It keeps playing in the background but you can’t see it anymore
because it renders the rest of the video as a black box. With the custom Optimized VideoView, the
videos will play on scroll in the ListView just like our Instagram, Facebook, Twitter.
Examples
Optimized VideoView in ListView
This the custom VideoView that you need to have it in your package.
<your.packagename.VideoView
android:id="@+id/video_view"
android:layout_width="300dp"
android:layout_height="300dp" />
package your.package.com.whateveritis;
import android.content.Context;
import android.content.Intent;
import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.MediaController;
import android.widget.MediaController.MediaPlayerControl;
import java.io.IOException;
/**
* VideoView is used to play video, just like
https://riptutorial.com/ 1014
* {@link android.widget.VideoView VideoView}. We define a custom view, because
* we could not use {@link android.widget.VideoView VideoView} in ListView. <br/>
* VideoViews inside ScrollViews do not scroll properly. Even if you use the
* workaround to set the background color, the MediaController does not scroll
* along with the VideoView. Also, the scrolling video looks horrendous with the
* workaround, lots of flickering.
*
* @author leo
*/
public class VideoView extends TextureView implements MediaPlayerControl {
https://riptutorial.com/ 1015
mContext = context;
initVideoView();
}
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
/*
* Parent says we can be as big as we want. Just don't be larger
* than max size imposed on ourselves.
*/
result = desiredSize;
break;
case MeasureSpec.AT_MOST:
/*
* Parent says we can be as big as we want, up to specSize. Don't be
* larger than specSize, and don't be larger than the max size
* imposed on ourselves.
*/
result = Math.min(desiredSize, specSize);
break;
case MeasureSpec.EXACTLY:
// No choice. Do what we are told.
result = specSize;
break;
}
return result;
}
https://riptutorial.com/ 1016
public Uri getUri() {
return mUri;
}
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mMediaPlayer.setOnCompletionListener(mCompleteListener);
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
mMediaPlayer.setOnInfoListener(mOnInfoListener);
mMediaPlayer.setOnVideoSizeChangedListener(mVideoSizeChangedListener);
mMediaPlayer.setSurface(mSurface);
mCurrentBufferPercentage = 0;
mMediaPlayer.setDataSource(mContext, mUri);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.prepareAsync();
mCurrentState = STATE_PREPARING;
} catch (IllegalStateException e) {
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
String msg = (e.getMessage()==null)?"":e.getMessage();
Log.i("",msg); // TODO auto-generated catch block
} catch (IOException e) {
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
String msg = (e.getMessage()==null)?"":e.getMessage();
Log.i("",msg); // TODO auto-generated catch block
}
}
https://riptutorial.com/ 1017
mMediaPlayer.release();
mMediaPlayer = null;
if (null != mMediaControllListener) {
mMediaControllListener.onStop();
}
}
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
// Will resize the view if the video dimensions have been found.
// video dimensions are found after onPrepared has been called by
// MediaPlayer
int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
if ((mVideoWidth > 0) && (mVideoHeight > 0)) {
if ((mVideoWidth * height) > (width * mVideoHeight)) {
Log.d(TAG, "Video too tall, change size.");
height = (width * mVideoHeight) / mVideoWidth;
} else if ((mVideoWidth * height) < (width * mVideoHeight)) {
Log.d(TAG, "Video too wide, change size.");
width = (height * mVideoWidth) / mVideoHeight;
} else {
Log.d(TAG, "Aspect ratio is correct.");
}
}
setMeasuredDimension(width, height);
}
https://riptutorial.com/ 1018
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (isInPlaybackState() && mMediaController != null) {
toggleMediaControlsVisiblity();
}
return false;
}
@Override
public boolean onTrackballEvent(MotionEvent ev) {
if (isInPlaybackState() && mMediaController != null) {
toggleMediaControlsVisiblity();
}
return false;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK && keyCode !=
KeyEvent.KEYCODE_VOLUME_UP && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN
&& keyCode != KeyEvent.KEYCODE_VOLUME_MUTE && keyCode != KeyEvent.KEYCODE_MENU
&& keyCode != KeyEvent.KEYCODE_CALL
&& keyCode != KeyEvent.KEYCODE_ENDCALL;
if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {
if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode ==
KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
if (mMediaPlayer.isPlaying()) {
pause();
mMediaController.show();
} else {
start();
mMediaController.hide();
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
if (!mMediaPlayer.isPlaying()) {
start();
mMediaController.hide();
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP || keyCode ==
KeyEvent.KEYCODE_MEDIA_PAUSE) {
if (mMediaPlayer.isPlaying()) {
pause();
mMediaController.show();
}
return true;
} else {
toggleMediaControlsVisiblity();
}
}
https://riptutorial.com/ 1019
}
}
@Override
public int getDuration() {
if (isInPlaybackState()) {
return mMediaPlayer.getDuration();
}
return -1;
}
@Override
public int getCurrentPosition() {
if (isInPlaybackState()) {
return mMediaPlayer.getCurrentPosition();
}
return 0;
}
@Override
public void seekTo(int msec) {
https://riptutorial.com/ 1020
if (isInPlaybackState()) {
mMediaPlayer.seekTo(msec);
mSeekWhenPrepared = 0;
} else {
mSeekWhenPrepared = msec;
}
}
@Override
public boolean isPlaying() {
return isInPlaybackState() && mMediaPlayer.isPlaying();
}
@Override
public int getBufferPercentage() {
if (mMediaPlayer != null) {
return mCurrentBufferPercentage;
}
return 0;
}
@Override
public boolean canPause() {
return false;
}
@Override
public boolean canSeekBackward() {
return false;
}
@Override
public boolean canSeekForward() {
return false;
}
@Override
public int getAudioSessionId() {
if (mAudioSession == 0) {
MediaPlayer foo = new MediaPlayer();
mAudioSession = foo.getAudioSessionId();
foo.release();
}
return mAudioSession;
}
// Listeners
private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = new
MediaPlayer.OnBufferingUpdateListener() {
@Override
public void onBufferingUpdate(final MediaPlayer mp, final int percent) {
mCurrentBufferPercentage = percent;
}
};
https://riptutorial.com/ 1021
MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(final MediaPlayer mp) {
mCurrentState = STATE_PLAYBACK_COMPLETED;
mTargetState = STATE_PLAYBACK_COMPLETED;
mSurface.release();
if (mMediaController != null) {
mMediaController.hide();
}
if (mOnCompletionListener != null) {
mOnCompletionListener.onCompletion(mp);
}
if (mMediaControllListener != null) {
mMediaControllListener.onComplete();
}
}
};
if (mOnPreparedListener != null) {
mOnPreparedListener.onPrepared(mMediaPlayer);
}
if (mMediaController != null) {
mMediaController.setEnabled(true);
//mMediaController.setAnchorView(getRootView());
}
mVideoWidth = mp.getVideoWidth();
mVideoHeight = mp.getVideoHeight();
requestLayout();
invalidate();
if ((mVideoWidth != 0) && (mVideoHeight != 0)) {
if (mTargetState == STATE_PLAYING) {
mMediaPlayer.start();
if (null != mMediaControllListener) {
mMediaControllListener.onStart();
}
}
} else {
if (mTargetState == STATE_PLAYING) {
mMediaPlayer.start();
if (null != mMediaControllListener) {
mMediaControllListener.onStart();
https://riptutorial.com/ 1022
}
}
}
}
};
if (mMediaController != null) {
mMediaController.hide();
}
/*
* Otherwise, pop up an error dialog so the user knows that
* something bad has happened. Only try and pop up the dialog if
* we're attached to a window. When we're going away and no longer
* have a window, don't bother showing the user an error.
*/
if (getWindowToken() != null) {
https://riptutorial.com/ 1023
SurfaceTextureListener mSurfaceTextureListener = new SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width,
final int height) {
Log.d(TAG, "onSurfaceTextureAvailable.");
mSurfaceTexture = surface;
openVideo();
}
@Override
public void onSurfaceTextureSizeChanged(final SurfaceTexture surface, final int width,
final int height) {
Log.d(TAG, "onSurfaceTextureSizeChanged: " + width + '/' + height);
mSurfaceWidth = width;
mSurfaceHeight = height;
boolean isValidState = (mTargetState == STATE_PLAYING);
boolean hasValidSize = (mVideoWidth == width && mVideoHeight == height);
if (mMediaPlayer != null && isValidState && hasValidSize) {
if (mSeekWhenPrepared != 0) {
seekTo(mSeekWhenPrepared);
}
start();
}
}
@Override
public boolean onSurfaceTextureDestroyed(final SurfaceTexture surface) {
mSurface = null;
if (mMediaController != null)
mMediaController.hide();
release(true);
return true;
}
@Override
public void onSurfaceTextureUpdated(final SurfaceTexture surface) {
}
};
/**
* Register a callback to be invoked when the media file is loaded and ready
* to go.
*
* @param l The callback that will be run
*/
public void setOnPreparedListener(MediaPlayer.OnPreparedListener l) {
mOnPreparedListener = l;
}
/**
* Register a callback to be invoked when the end of a media file has been
* reached during playback.
*
* @param l The callback that will be run
*/
public void setOnCompletionListener(OnCompletionListener l) {
mOnCompletionListener = l;
}
https://riptutorial.com/ 1024
/**
* Register a callback to be invoked when an error occurs during playback or
* setup. If no listener is specified, or if the listener returned false,
* VideoView will inform the user of any errors.
*
* @param l The callback that will be run
*/
public void setOnErrorListener(OnErrorListener l) {
mOnErrorListener = l;
}
/**
* Register a callback to be invoked when an informational event occurs
* during playback or setup.
*
* @param l The callback that will be run
*/
public void setOnInfoListener(OnInfoListener l) {
mOnInfoListener = l;
}
MediaControllListener mMediaControllListener;
@Override
public void setVisibility(int visibility) {
System.out.println("setVisibility: " + visibility);
super.setVisibility(visibility);
}
}
Help from this gitub repository. Though It has some issues as it was written 3 years ago I
managed to fix them on my own as written above.
https://riptutorial.com/ 1025
Chapter 181: Orientation Changes
Remarks
Reference : https://guides.codepath.com/android/Handling-Configuration-Changes#references
Examples
Saving and Restoring Activity State
As your activity begins to stop, the system calls onSaveInstanceState() so your activity can save
state information with a collection of key-value pairs. The default implementation of this method
automatically saves information about the state of the activity's view hierarchy, such as the text in
an EditText widget or the scroll position of a ListView.
To save additional state information for your activity, you must implement onSaveInstanceState()
and add key-value pairs to the Bundle object. For example:
@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
// Save custom values into the bundle
savedInstanceState.putInt(SOME_VALUE, someIntValue);
savedInstanceState.putString(SOME_OTHER_VALUE, someStringValue);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
}
The system will call that method before an Activity is destroyed. Then later the system will call
onRestoreInstanceState where we can restore state from the bundle:
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
someIntValue = savedInstanceState.getInt(SOME_VALUE);
someStringValue = savedInstanceState.getString(SOME_OTHER_VALUE);
}
Instance state can also be restored in the standard Activity#onCreate method but it is convenient
to do it in onRestoreInstanceState which ensures all of the initialization has been done and allows
subclasses to decide whether to use the default implementation. Read this stackoverflow post for
details.
https://riptutorial.com/ 1026
Note that onSaveInstanceState and onRestoreInstanceState are not guaranteed to be called together.
Android invokes onSaveInstanceState() when there's a chance the activity might be destroyed.
However, there are cases where onSaveInstanceState is called but the activity is not destroyed and
as a result onRestoreInstanceState is not invoked.
Fragments also have a onSaveInstanceState() method which is called when their state needs to be
saved:
// Fires when a configuration change occurs and fragment needs to save state
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(SOME_VALUE_KEY, someStateValue);
super.onSaveInstanceState(outState);
}
}
For the fragment state to be saved properly, we need to be sure that we aren't unnecessarily
recreating the fragment on configuration changes. This means being careful not to reinitialize
existing fragments when they already exist. Any fragments being initialized in an Activity need to
be looked up by tag after a configuration change:
@Override
protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) { // saved instance state, fragment may exist
// look up the instance that already exists by tag
fragmentSimple = (MySimpleFragment)
https://riptutorial.com/ 1027
getSupportFragmentManager().findFragmentByTag(SIMPLE_FRAGMENT_TAG);
} else if (fragmentSimple == null) {
// only create fragment if they haven't been instantiated already
fragmentSimple = new MySimpleFragment();
}
}
}
This requires us to be careful to include a tag for lookup whenever putting a fragment into the
activity within a transaction:
@Override
protected void onCreate(Bundle savedInstanceState) {
// ... fragment lookup or instantation from above...
// Always add a tag to a fragment being inserted into container
if (!fragmentSimple.isInLayout()) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, fragmentSimple, SIMPLE_FRAGMENT_TAG)
.commit();
}
}
}
With this simple pattern, we can properly re-use fragments and restore their state across
configuration changes.
Retaining Fragments
In many cases, we can avoid problems when an Activity is re-created by simply using fragments. If
your views and state are within a fragment, we can easily have the fragment be retained when the
activity is re-created:
https://riptutorial.com/ 1028
}
This approach keeps the fragment from being destroyed during the activity lifecycle. They are
instead retained inside the Fragment Manager. See the Android official docs for more information.
Now you can check to see if the fragment already exists by tag before creating one and the
fragment will retain it's state across configuration changes. See the Handling Runtime Changes
guide for more details.
If you want to lock the screen orientation change of any screen (activity) of your android
application you just need to set the android:screenOrientation property of an <activity> within the
AndroidManifest.xml:
<activity
android:name="com.techblogon.screenorientationexample.MainActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
<!-- ... -->
</activity>
If your application doesn't need to update resources during a specific configuration change and
you have a performance limitation that requires you to avoid the activity restart, then you can
declare that your activity handles the configuration change itself, which prevents the system from
restarting your activity.
However, this technique should be considered a last resort when you must avoid restarts due to a
configuration change and is not recommended for most applications. To take this approach, we
must add the android:configChanges node to the activity within the AndroidManifest.xml:
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:label="@string/app_name">
Now, when one of these configurations change, the activity does not restart but instead receives a
call to onConfigurationChanged():
https://riptutorial.com/ 1029
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
}
See the Handling the Change docs. For more about which configuration changes you can handle
in your activity, see the android:configChanges documentation and the Configuration class.
Handling AsyncTask
Problem:
• If after the AsyncTask starts there is a screen rotation the owning activity is destroyed and
recreated.
• When the AsyncTask finishes it wants to update the UI that may not valid anymore.
Solution:
Using Loaders, one can easily overcome the activity destruction/recreation.
Example:
MainActivity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(loaderManager.getLoader(MY_LOADER) == null) {
loaderManager.initLoader(MY_LOADER, null, this).forceLoad();
}
}
@Override
public Loader<Bitmap> onCreateLoader(int id, Bundle args) {
//Create a new instance of your Loader<Bitmap>
MyLoader loader = new MyLoader(MainActivity.this);
return loader;
}
https://riptutorial.com/ 1030
@Override
public void onLoadFinished(Loader<Bitmap> loader, Bitmap data) {
// do something in the parent activity/service
// i.e. display the downloaded image
Log.d("MyAsyncTask", "Received result: ");
}
@Override
public void onLoaderReset(Loader<Bitmap> loader) {
}
}
AsyncTaskLoader:
@Override
public Bitmap loadInBackground() {
// Do work. I.e download an image from internet to be displayed in gui.
// i.e. return the downloaded gui
return result;
}
}
Note:
It is important to use either the v4 compatibility library or not, but do not use part of one and part of
the other, as it will lead to compilation errors. To check you can look at the imports for
android.support.v4.content and android.content (you shouldn't have both).
It is very common that during development, one may find very useful to lock/unlock the device
screen during specific parts of the code.
For instance, while showing a Dialog with information, the developer might want to lock the
screen's rotation to prevent the dialog from being dismissed and the current activity from being
rebuilt to unlock it again when the dialog is dismissed.
Even though we can achieve rotation locking from the manifest by doing :
<activity
android:name=".TheActivity"
android:screenOrientation="portrait"
https://riptutorial.com/ 1031
android:label="@string/app_name" >
</activity>
And then calling the following, to respectively lock and unlock the device rotation
lockDeviceRotation(true)
and
lockDeviceRotation(false)
https://riptutorial.com/ 1032
Chapter 182: ORMLite in android
Examples
Android OrmLite over SQLite example
ORMLite is an Object Relational Mapping package that provides simple and lightweight
functionality for persisting Java objects to SQL databases while avoiding the complexity and
overhead of more standard ORM packages.
Speaking for Android, OrmLite is implemented over the out-of-the-box supported database,
SQLite. It makes direct calls to the API to access SQLite.
Gradle setup
To get started you should include the package to the build gradle.
// https://mvnrepository.com/artifact/com.j256.ormlite/ormlite-android
compile group: 'com.j256.ormlite', name: 'ormlite-android', version: '5.0'
POJO configuration
Then you should configure a POJO to be persisted to the database. Here care must be taken to
the annotations:
• Add the @DatabaseTable annotation to the top of each class. You can also use @Entity.
• Add the @DatabaseField annotation right before each field to be persisted. You can also
use @Column and others.
• Add a no-argument constructor to each class with at least package visibility.
@DatabaseTable(tableName = "form_model")
public class FormModel implements Serializable {
@DatabaseField(generatedId = true)
private Long id;
@DatabaseField(dataType = DataType.SERIALIZABLE)
ArrayList<ReviewItem> reviewItems;
@DatabaseField(index = true)
private String username;
@DatabaseField
private String createdAt;
public FormModel() {
}
https://riptutorial.com/ 1033
this.createdAt = createdAt;
}
}
More information about the annotation can be found at the official documentation.
Database Helper
To continue with, you will need to create a database helper class which should extend the
OrmLiteSqliteOpenHelper class.
This class creates and upgrades the database when your application is installed and can also
provide the DAO classes used by your other classes.
DAO stands for Data Access Object and it provides all the scrum functionality and specializes in
the handling a single persisted class.
onUpgrade handles the upgrading of the database tables when you upgrade your app to a
new version
//Database name
private static final String DATABASE_NAME = "gaia";
//Version of the database. Changing the version will call {@Link OrmLite.onUpgrade}
private static final int DATABASE_VERSION = 2;
/**
* The data access object used to interact with the Sqlite database to do C.R.U.D
operations.
*/
private Dao<FormModel, Long> todoDao;
https://riptutorial.com/ 1034
public OrmLite(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION,
/**
* R.raw.ormlite_config is a reference to the ormlite_config2.txt file in
the
* /res/raw/ directory of this project
* */
R.raw.ormlite_config2);
}
@Override
public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) {
try {
/**
* creates the database table
*/
TableUtils.createTable(connectionSource, FormModel.class);
} catch (SQLException e) {
e.printStackTrace();
} catch (java.sql.SQLException e) {
e.printStackTrace();
}
}
/*
It is called when you construct a SQLiteOpenHelper with version newer than the
version of the opened database.
*/
@Override
public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource,
int oldVersion, int newVersion) {
try {
/**
* Recreates the database when onUpgrade is called by the framework
*/
TableUtils.dropTable(connectionSource, FormModel.class, false);
onCreate(database, connectionSource);
/**
* Returns an instance of the data access object
* @return
* @throws SQLException
*/
public Dao<FormModel, Long> getDao() throws SQLException {
if(todoDao == null) {
try {
todoDao = getDao(FormModel.class);
} catch (java.sql.SQLException e) {
e.printStackTrace();
}
}
return todoDao;
}
}
https://riptutorial.com/ 1035
Persisting Object to SQLite
Finally, the class that persists the object to the database.
try {
simpleDao.create(form);
} catch (SQLException e) {
e.printStackTrace();
}
List<FormModel> list = null;
try {
// query for all of the data objects in the database
list = simpleDao.queryForAll();
} catch (SQLException e) {
e.printStackTrace();
}
// our string builder for building the content-view
StringBuilder sb = new StringBuilder();
int simpleC = 1;
for (FormModel simple : list) {
sb.append('#').append(simpleC).append(":
").append(simple.getUsername()).append('\n');
simpleC++;
}
System.out.println(sb.toString());
}
The accessor of the DOA at the constructor of the above class is defined as:
/*
Provides the SQLite Helper Object among the application
https://riptutorial.com/ 1036
*/
public OrmLite getHelper() {
if (dbHelper == null) {
dbHelper = OpenHelperManager.getHelper(this, OrmLite.class);
}
return dbHelper;
}
https://riptutorial.com/ 1037
Chapter 183: Otto Event Bus
Remarks
Otto is deprecated in favor of RxJava and RxAndroid. These projects permit the same event-driven
programming model as Otto, but they’re more capable and offer better control of threading.
Examples
Passing an event
This example describes passing an event using the Otto Event Bus.
To use the Otto Event Bus in Android Studio you have to insert the following statement in your
modules gradle file:
dependencies {
compile 'com.squareup:otto:1.3.8'
}
import com.squareup.otto.Bus;
private BusProvider() {
}
}
To send an event we only need our BusProvider and it's post method. Here we send an event if
the action of an AsyncTask is completed:
https://riptutorial.com/ 1038
...
@Override
protected void onPostExecute(Void param) {
BusProvider.getInstance().post(
new DatabaseContentChangedEvent("Content changed")
);
}
}
Receiving an event
To receive an event it is necessary to implement a method with the event type as parameter and
annotate it using @Subscribe. Furthermore you have to register/unregister the instance of your
object at the BusProvider (see example Sending an event):
...
@Override
public void onResume() {
super.onResume();
BusProvider.getInstance().register(this);
}
@Override
public void onPause() {
super.onPause();
BusProvider.getInstance().unregister(this);
}
@Subscribe
public void onDatabaseContentChanged(DatabaseContentChangedEvent event) {
Log.i(TAG, "onDatabaseContentChanged: "+event.message);
}
}
Important: In order to receive that event an instance of the class has to exist. This is usually not
the case when you want to send a result from one activity to another activity. So check your use
case for the event bus.
https://riptutorial.com/ 1039
Chapter 184: PackageManager
Examples
Retrieve application version
return info.versionName;
}
To get versionName and versionCode of current build of your application you should query Android's
package manager.
try {
// Reference to Android's package manager
PackageManager packageManager = this.getPackageManager();
// Version code
info.versionCode
// Version name
info.versionName
} catch (NameNotFoundException e) {
// Handle the exception
}
To get the time at which your app was installed or updated, you should query Android's package
manager.
try {
// Reference to Android's package manager
PackageManager packageManager = this.getPackageManager();
https://riptutorial.com/ 1040
// Last update time. Units are as per currentTimeMillis().
info.lastUpdateTime
} catch (NameNotFoundException e) {
// Handle the exception
}
Below method will help to get the app name using package name
Below method will help to get the app icon using package name,
return appIcon;
}
List<ApplicationInfo> list =
packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
return list;
}
Below method will help to hide the app icon from the launcher.
https://riptutorial.com/ 1041
ComponentName componentName = new ComponentName(context.getApplicationContext(),
SplashActivity.class);
if (current != setting) {
context.getPackageManager().setComponentEnabledSetting(componentName, setting,
PackageManager.DONT_KILL_APP);
}
}
Note: After switch off the device and switch on this icon will come back in the launcher.
https://riptutorial.com/ 1042
Chapter 185: Pagination in RecyclerView
Introduction
Pagination is a common issue with for a lot of mobile apps that need to deal with lists of data. Most
of the mobile apps are now starting to take up the "endless page" model, where scrolling
automatically loads in new content. CWAC Endless Adapter makes it really easy to use this
pattern in Android applications
Examples
MainActivity.java
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.widget.TextView;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
https://riptutorial.com/ 1043
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pageNumber = 1;
toolbar = (Toolbar) findViewById(R.id.toolbar);
tvEmptyView = (TextView) findViewById(R.id.empty_view);
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
studentList = new ArrayList<>();
mTempCheck=new ArrayList<>();
handler = new Handler();
if (toolbar != null) {
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("Android Students");
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new DataAdapter(studentList, mRecyclerView);
mRecyclerView.setAdapter(mAdapter);
GetGroupData("" + mStart, "" + mEnd);
mAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore() {
if( mTempCheck.size()> 0) {
studentList.add(null);
mAdapter.notifyItemInserted(studentList.size() - 1);
int start = pageNumber * 20;
start = start + 1;
++ pageNumber;
mTempCheck.clear();
GetData("" + start,""+ mEnd);
}
}
});
}
@Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d("ResponseErrorVolly: " + error.getMessage());
}});
}
// load initial data
https://riptutorial.com/ 1044
private void loadData(int start,int end,boolean notifyadapter) {
for (int i = start; i <= end; i++) {
studentList.add(new Student("Student " + i, "androidstudent" + i + "@gmail.com"));
if(notifyadapter)
mAdapter.notifyItemInserted(studentList.size());
}
}
}
OnLoadMoreListener.java
DataAdapter.java
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import java.util.List;
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
https://riptutorial.com/ 1045
loading = true;
}
}
});
}
}
@Override
public int getItemViewType(int position) {
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder vh;
if (viewType == VIEW_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_row,
parent, false);
vh = new StudentViewHolder(v);
} else {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.progress_item,
parent, false);
vh = new ProgressViewHolder(v);
}
return vh;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof StudentViewHolder) {
Student singleStudent=studentList.get(position);
((StudentViewHolder) holder).tvName.setText(singleStudent.getName());
((StudentViewHolder) holder).tvEmailId.setText(singleStudent.getEmailId());
((StudentViewHolder) holder).student= singleStudent;
} else {
((ProgressViewHolder) holder).progressBar.setIndeterminate(true);
}
}
@Override
public int getItemCount() {
return studentList.size();
}
//
public static class StudentViewHolder extends RecyclerView.ViewHolder {
public TextView tvName;
https://riptutorial.com/ 1046
public StudentViewHolder(View v) {
super(v);
tvName = (TextView) v.findViewById(R.id.tvName);
tvEmailId = (TextView) v.findViewById(R.id.tvEmailId);
}
}
public ProgressViewHolder(View v) {
super(v);
progressBar = (ProgressBar) v.findViewById(R.id.progressBar1);
}
}
https://riptutorial.com/ 1047
Chapter 186: Paint
Introduction
A paint is one of the four objects needed to draw, along with a Canvas (holds drawing calls), a
Bitmap (holds the pixels), and a drawing primitive (Rect, Path, Bitmap...)
Examples
Creating a Paint
It is generally suggested to never create a paint object, or any other object in onDraw() as it can
lead to performance issues. (Android Studio will probably warn you) Instead, make it global and
initialize it in your class constructor like so:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(0xFF000000);
// ...
}
}
https://riptutorial.com/ 1048
a negative value will tighten the text, while a positive one will expand it.
• setTextAlign(Paint.Align align) Set text alignment relative to its origin. Paint.Align.LEFT will
draw it to the right of the origin, RIGHT will draw it to the left, and CENTER will draw it centered
on the origin (horizontally)
• setTextSkewX(float skewX) This could be considered as fake italic. SkewX represents the
horizontal offset of the text bottom. (use -0.25 for italic)
• setStyle(Paint.Style style) Fill text FILL, Stroke text STROKE, or both FILL_AND_STROKE
Measuring text
• float width = paint.measureText(String text) Measure the width of text
• float height = paint.ascent() Measure the height of text
• paint.getTextBounds(String text, int start, int end, Rect bounds Stores the text
dimensions. You have allocate the Rect, it cannot be null:
There are other methods for measuring, however these three should fit most purposes.
• setStyle(Paint.Style style) Filled shape FILL, Stroke shape STROKE, or both FILL_AND_STROKE
• setColor(int color) Set the paint drawing color. You can also use setARGB(int a, int r, int
g, int b and setAlpha(int alpha)
• setStrokeCap(Paint.Cap cap) Set line caps, either ROUND, SQUARE, or BUTT (none) See this.
• setStrokeJoin(Paint.Join join) Set line joins, either MITER (pointy), ROUND, or BEVEL. See this.
• setStrokeMiter(float miter) Set miter join limit. This can prevent miter join from going on
indefinitively, turning it into a bevel join after x pixels. See this.
• setStrokeWidth(float width) Set stroke width. 0 will draw in hairline mode, independant of the
canvas matrix. (always 1 pixel)
Setting flags
You can set the following flags in the constructor, or with setFlags(int flags)
https://riptutorial.com/ 1049
• Paint.HINTING_OFF, Paint.HINTING_ON Toggles font hinting, see this
• Paint.LINEAR_TEXT_FLAG Disables font scaling, draw operations are scaled instead
• Paint.SUBPIXEL_TEXT_FLAG Text will be computed using subpixel accuracy.
• Paint.STRIKE_THRU_TEXT_FLAG Text drawn will be striked
• Paint.UNDERLINE_TEXT_FLAG Text drawn will be underlined
Trying to remove a flag that isn't there or adding a flag that is already there won't change anything.
Also note that most flags can also be set using set<Flag>(boolean enabled), for example
setAntialias(true).
You can use paint.reset() to reset the paint to its default settings. The only default flag is
EMBEDDED_BITMAP_TEXT_FLAG. It will be set even if you use new Paint(0), you will have
https://riptutorial.com/ 1050
Chapter 187: Parcelable
Introduction
Parcelable is an Android specific interface where you implement the serialization yourself. It was
created to be far more efficient that Serializable, and to get around some problems with the default
Java serialization scheme.
Remarks
It is important to remember that the order in which you write fields into a Parcel MUST BE THE
SAME ORDER that you read them out from the parcel when constructing your custom object.
The parcelable interface has a strict 1 MB size limit. That means that any object, or combinations
of objects, you put into a parcel that take up over 1MB of space will become corrupted on the other
side. This can be hard to discover, so keep in mind what kind of objects you plan to make
parcelable. If they have large dependency trees, consider another way in which to pass data
around.
Examples
Making a custom object Parcelable.
/**
* Created by Alex Sullivan on 7/21/16.
*/
public class Foo implements Parcelable
{
private final int myFirstVariable;
private final String mySecondVariable;
private final long myThirdVariable;
// Note that you MUST read values from the parcel IN THE SAME ORDER that
// values were WRITTEN to the parcel! This method is our own custom method
// to instantiate our object from a Parcel. It is used in the Parcelable.Creator variable
we declare below.
public Foo(Parcel in)
{
this.myFirstVariable = in.readInt();
this.mySecondVariable = in.readString();
this.myThirdVariable = in.readLong();
}
https://riptutorial.com/ 1051
// The describe contents method can normally return 0. It's used when
// the parceled object includes a file descriptor.
@Override
public int describeContents()
{
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags)
{
dest.writeInt(myFirstVariable);
dest.writeString(mySecondVariable);
dest.writeLong(myThirdVariable);
}
// Note that this seemingly random field IS NOT OPTIONAL. The system will
// look for this variable using reflection in order to instantiate your
// parceled object when read from an Intent.
public static final Parcelable.Creator<Foo> CREATOR = new Parcelable.Creator<Foo>()
{
// This method is used to actually instantiate our custom object
// from the Parcel. Convention dictates we make a new constructor that
// takes the parcel in as its only argument.
public Foo createFromParcel(Parcel in)
{
return new Foo(in);
}
https://riptutorial.com/ 1052
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeParcelable(owner, flags);
dest.writeByte((byte) (isPrivate ? 1 : 0));
}
@Override
public int describeContents() {
return 0;
}
@Override
public Repository[] newArray(int size) {
return new Repository[size];
}
};
/**
* Created by Nick Cardoso on 03/08/16.
* This is not a complete parcelable implementation, it only highlights the easiest
* way to read and write your Enum values to your parcel
https://riptutorial.com/ 1053
*/
public class Foo implements Parcelable {
...
@Override
public void writeToParcel(Parcel dest, int flags) {
This is preferable to (for example) using an ordinal, because inserting new values into your enum
will not affect previously stored values
https://riptutorial.com/ 1054
Chapter 188: Performance Optimization
Introduction
Your Apps performance is a crucial element of the user experience. Try to avoid bad performing
patterns like doing work on the UI thread and learn how to write fast and responsive apps.
Examples
Save View lookups with the ViewHolder pattern
Especially in a ListView, you can run into performance problems by doing too many findViewById()
calls during scrolling. By using the ViewHolder pattern, you can save these lookups and improve
your ListView performance.
If your list item contains a single TextView, create a ViewHolder class to store the instance:
While creating your list item, attach a ViewHolder object to the list item:
return convertView;
}
Using this pattern, findViewById() will only be called when a new View is being created and the
ListView can recycle your views much more efficiently.
https://riptutorial.com/ 1055
Chapter 189: Picasso
Introduction
Picasso is an image library for Android. It's created and maintained by Square. It simplifies the
process of displaying images from external locations. The library handles every stage of the
process, from the initial HTTP request to the caching of the image. In many cases, only a few lines
of code are required to implement this neat library.
Remarks
Picasso is a powerful image downloading and caching library for Android.
Follow this example to add the library to your project.
Websites:
• Source
• Doc
• Change Log
Examples
Adding Picasso Library to your Android Project
Gradle.
dependencies {
compile "com.squareup.picasso:picasso:2.5.2"
}
Maven:
<dependency>
<groupId>com.squareup.picasso</groupId>
<artifactId>picasso</artifactId>
<version>2.5.2</version>
</dependency>
Picasso supports both download and error placeholders as optional features. Its also provides
https://riptutorial.com/ 1056
callbacks for handling the download result.
Picasso.with(context)
.load("YOUR IMAGE URL HERE")
.placeholder(Your Drawable Resource) //this is optional the image to display while the url
image is downloading
.error(Your Drawable Resource) //this is also optional if some error has occurred in
downloading the image this image would be displayed
.into(imageView, new Callback(){
@Override
public void onSuccess() {}
@Override
public void onError() {}
});
A request will be retried three times before the error placeholder is shown.
Picasso.with(context)
.load("YOUR IMAGE URL HERE")
.placeholder(DRAWABLE RESOURCE) // optional
.error(DRAWABLE RESOURCE) // optional
.resize(width, height) // optional
.rotate(degree) // optional
.into(imageView);
Here is an example Picasso Circle Transform class based on the original, with the addition of a
thin border, and also includes functionality for an optional separator for stacking:
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import com.squareup.picasso.Transformation;
public CircleTransform(){
}
@Override
public Bitmap transform(Bitmap source) {
int size = Math.min(source.getWidth(), source.getHeight());
https://riptutorial.com/ 1057
int x = (source.getWidth() - size) / 2;
int y = (source.getHeight() - size) / 2;
if (squaredBitmap != source) {
source.recycle();
}
float r = size/2f;
canvas.drawCircle(r, r, r-1, paint);
squaredBitmap.recycle();
return bitmap;
}
@Override
public String key() {
return "circle";
}
}
Here is how to use it when loading an image (assuming this is an Activity Context, and url is a
String with the url of the image to load):
https://riptutorial.com/ 1058
Result:
For use with the separator, give true to the constructor for the top image:
Picasso.with(context)
.load(uri)
.networkPolicy(NetworkPolicy.NO_CACHE)
.memoryPolicy(MemoryPolicy.NO_CACHE)
.placeholder(R.drawable.placeholder)
.into(imageView);
Picasso.with(context)
.load(new File(imagePath))
.into(imageView);
If you want to Download image as Bitmap using Picasso following code will help you:
Picasso.with(mContext)
.load(ImageUrl)
https://riptutorial.com/ 1059
.into(new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
// Todo: Do something with your bitmap here
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
In certain cases we need to cancel an image download request in Picasso before the download
has completed.
This could happen for various reasons, for example if the parent view transitioned to some other
view before the image download could be completed.
In this case, you can cancel the image download request using the cancelRequest() method:
ImageView imageView;
//......
Picasso.with(imageView.getContext()).cancelRequest(imageView);
@Override
public Drawable getDrawable(String source) {
Log.d(PicassoImageGetter.class.getName(), "Start loading url " + source);
picasso
.load(source)
.error(R.drawable.connection_error)
https://riptutorial.com/ 1060
.into(drawable);
return drawable;
}
@Override
public void draw(final Canvas canvas) {
if (drawable != null) {
checkBounds();
drawable.draw(canvas);
}
}
drawable.setBounds(
halfOfPlaceHolderWidth - halfOfImageWidth, //centering an image
0,
halfOfPlaceHolderWidth + halfOfImageWidth,
height);
//------------------------------------------------------------------//
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
setDrawable(new BitmapDrawable(Application.getContext().getResources(), bitmap));
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
setDrawable(errorDrawable);
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
https://riptutorial.com/ 1061
setDrawable(placeHolderDrawable);
}
//------------------------------------------------------------------//
}
}
Try offline disk cache first, then go online and fetch the image
first add the OkHttp to the gradle build file of the app module
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.squareup.okhttp:okhttp:2.4.0'
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.0.2'
import android.app.Application;
import com.squareup.picasso.OkHttpDownloader;
import com.squareup.picasso.Picasso;
}
}
<application
android:name=".Global"
.. >
</application>
Normal Usage
https://riptutorial.com/ 1062
Picasso.with(getActivity())
.load(imageUrl)
.networkPolicy(NetworkPolicy.OFFLINE)
.into(imageView, new Callback() {
@Override
public void onSuccess() {
//Offline Cache hit
}
@Override
public void onError() {
//Try again online if cache failed
Picasso.with(getActivity())
.load(imageUrl)
.error(R.drawable.header)
.into(imageView, new Callback() {
@Override
public void onSuccess() {
//Online download
}
@Override
public void onError() {
Log.v("Picasso","Could not fetch image");
}
});
}
});
https://riptutorial.com/ 1063
Chapter 190: Ping ICMP
Introduction
The ICMP Ping request can be performed in Android by creating a new process to run the ping
request. The outcome of the request can be evaluated upon the completion of the ping request
from within its process.
Examples
Performs a single Ping
This example attempts a single Ping request. The ping command inside the runtime.exec method
call can be modified to any valid ping command you might perform yourself in the command line.
try {
Process ipProcess = runtime.exec("/system/bin/ping -c 1 8.8.8.8");
int exitValue = ipProcess.waitFor();
ipProcess.destroy();
if(exitValue == 0){
// Success
} else {
// Failure
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
https://riptutorial.com/ 1064
Chapter 191: Port Mapping using Cling library
in Android
Examples
Adding Cling Support to your Android Project
build.gradle
repositories {
maven { url 'http://4thline.org/m2' }
}
dependencies {
// Cling
compile 'org.fourthline.cling:cling-support:2.1.0'
//creates a port mapping configuration with the external/internal port, an internal host IP,
the protocol and an optional description
PortMapping[] desiredMapping = new PortMapping[2];
desiredMapping[0] = new PortMapping(port,myIp, PortMapping.Protocol.TCP);
desiredMapping[1] = new PortMapping(port,myIp, PortMapping.Protocol.UDP);
https://riptutorial.com/ 1065
.nextElement();
Enumeration<InetAddress> enumInetAddress = networkInterface
.getInetAddresses();
while (enumInetAddress.hasMoreElements()) {
InetAddress inetAddress = enumInetAddress.nextElement();
if (inetAddress.isSiteLocalAddress()) {
ip +=inetAddress.getHostAddress();
}
}
}
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
ip += "Something Wrong! " + e.toString() + "\n";
}
return ip;
}
https://riptutorial.com/ 1066
Chapter 192: PorterDuff Mode
Introduction
PorterDuff is described as a way of combining images as if they were "irregular shaped pieces of
cardboard" overlayed on each other, as well as a scheme for blending the overlapping parts
Remarks
"Porter Duff" in itself is an alpha compositing technique named after a paper by Thomas Porter
and Tom Duff.
To summarize, the technique takes two images with alpha channel and generates the output
image by combining pixels values of two images. The various combining modes result in different
output image. For example, in following image, blue shape (source, existing pixels) is combined
with Yellow shape (destination, new pixels) in different modes:
https://riptutorial.com/ 1067
Examples
Creating a PorterDuff ColorFilter
PorterDuff.Mode is used to create a PorterDuffColorFilter. A color filter modifies the color of each
pixel of a visual resource.
The above filter will tint the non-transparent pixels to blue color.
drawable.setColorFilter(filter);
https://riptutorial.com/ 1068
It can be applied to an ImageView:
imageView.setColorFilter(filter);
Also, it can be applied to a Paint, so that the color that is drawn using that paint, is modified by the
filter:
paint.setColorFilter(filter);
An Xfermode (think "transfer" mode) works as a transfer step in drawing pipeline. When an Xfermode
is applied to a Paint, the pixels drawn with the paint are combined with underlying pixels (already
drawn) as per the mode:
paint.setColor(Color.BLUE);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
Now we have a blue tint paint. Any shape drawn will tint the already existing, non-transparent
pixels blue in the area of the shape.
/**
* Apply a radial mask (vignette, i.e. fading to black at the borders) to a bitmap
* @param imageToApplyMaskTo Bitmap to modify
*/
public static void radialMask(final Bitmap imageToApplyMaskTo) {
Canvas canvas = new Canvas(imageToApplyMaskTo);
https://riptutorial.com/ 1069
Chapter 193: ProgressBar
Remarks
Official Documentation: ProgressBar
Examples
Indeterminate ProgressBar
<ProgressBar
android:id="@+id/progressBar"
android:indeterminate="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ProgressBar
android:id="@+id/progressBar"
android:indeterminate="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@android:style/Widget.ProgressBar.Horizontal"/>
style="@android:style/Widget.ProgressBar.Small"
style="@android:style/Widget.ProgressBar.Large"
style="@android:style/Widget.ProgressBar.Inverse"
style="@android:style/Widget.ProgressBar.Small.Inverse"
style="@android:style/Widget.ProgressBar.Large.Inverse"
Determinate ProgressBar
A determinate ProgressBar shows the current progress towards a specific maximum value.
https://riptutorial.com/ 1070
<ProgressBar
android:id="@+id/progressBar"
android:indeterminate="false"
android:layout_width="match_parent"
android:layout_height="10dp"
style="@android:style/Widget.ProgressBar.Horizontal"/>
<ProgressBar
android:id="@+id/progressBar"
android:indeterminate="false"
android:layout_width="10dp"
android:layout_height="match_parent"
android:progressDrawable="@drawable/progress_vertical"
style="@android:style/Widget.ProgressBar.Horizontal"/>
res/drawable/progress_vertical.xml
<ProgressBar
android:id="@+id/progressBar"
android:indeterminate="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progressDrawable="@drawable/progress_ring"
style="@android:style/Widget.ProgressBar.Horizontal"/>
res/drawable/progress_ring.xml
https://riptutorial.com/ 1071
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/secondaryProgress">
<shape
android:shape="ring"
android:useLevel="true"
android:thicknessRatio="24"
android:innerRadiusRatio="2.2">
<corners android:radius="3dp"/>
<solid android:color="#0000FF"/>
</shape>
</item>
<item android:id="@android:id/progress">
<shape
android:shape="ring"
android:useLevel="true"
android:thicknessRatio="24"
android:innerRadiusRatio="2.2">
<corners android:radius="3dp"/>
<solid android:color="#FFFFFF"/>
</shape>
</item>
</layer-list>
Customized progressbar
CustomProgressBarActivity.java:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_progressbar);
https://riptutorial.com/ 1072
progressBar.setProgress(pStatus);
txtProgress.setText(pStatus + " %");
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
pStatus++;
}
}
}).start();
}
}
activity_custom_progressbar.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.skholingua.android.custom_progressbar_circular.MainActivity" >
<RelativeLayout
android:layout_width="wrap_content"
android:layout_centerInParent="true"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerInParent="true"
android:indeterminate="false"
android:max="100"
android:progress="0"
android:progressDrawable="@drawable/custom_progressbar_drawable"
android:secondaryProgress="0" />
<TextView
android:id="@+id/txtProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/progressBar"
android:layout_centerInParent="true"
android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</RelativeLayout>
https://riptutorial.com/ 1073
custom_progressbar_drawable.xml:
<shape
android:shape="ring"
android:useLevel="false" >
<gradient
android:centerY="0.5"
android:endColor="#FA5858"
android:startColor="#0099CC"
android:type="sweep"
android:useLevel="false" />
</shape>
</rotate>
Reference screenshot:
https://riptutorial.com/ 1074
Tinting ProgressBar
Using an AppCompat theme, the ProgressBar's color will be the colorAccent you have defined.
https://riptutorial.com/ 1075
5.0
To change the ProgressBar color without changing the accent color you can use theandroid:theme
attribute overriding the accent color:
<ProgressBar
android:theme="@style/MyProgress"
style="@style/Widget.AppCompat.ProgressBar" />
To tint the ProgressBar you can use in the xml file the attributes android:indeterminateTintMode and
android:indeterminateTint
<ProgressBar
android:indeterminateTintMode="src_in"
android:indeterminateTint="@color/my_color"
/>
A linear progress indicator should always fill from 0% to 100% and never decrease in
value.
It should be represented by bars on the edge of a header or sheet that appear and
disappear.
<ProgressBar
android:id="@+id/my_progressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
https://riptutorial.com/ 1076
Indeterminate
To create indeterminate ProgressBar set the android:indeterminate attribute to true.
<ProgressBar
android:id="@+id/my_progressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"/>
https://riptutorial.com/ 1077
Determinate
To create determinate ProgressBar set the android:indeterminate attribute to false and use the
android:max and the android:progress attributes:
<ProgressBar
android:id="@+id/my_progressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:indeterminate="false"
android:max="100"
android:progress="10"/>
Buffer
To create a buffer effect with the ProgressBar set the android:indeterminate attribute to false and
use the android:max, the android:progress and the android:secondaryProgress attributes:
<ProgressBar
android:id="@+id/my_progressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="false"
android:max="100"
android:progress="10"
android:secondaryProgress="25"/>
<ProgressBar
android:id="@+id/progressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
https://riptutorial.com/ 1078
android:indeterminate="true"/>
Then when you need to switch from indeterminate to determinate progress use setIndeterminate()
method .
By Creating Custom Progress Dialog class, the dialog can be used to show in UI instance, without
recreating the dialog.
CustomProgress.java
https://riptutorial.com/ 1079
Now creating the custom progress layout
prograss_bar_dialog.xml
<TextView
android:id="@+id/progress_text"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_above="@+id/progress_bar"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:background="@android:color/transparent"
android:gravity="center_vertical"
android:text=""
android:textColor="@android:color/white"
android:textSize="16sp"
android:visibility="gone" />
<-- Where the style can be changed to any kind of ProgressBar -->
<ProgressBar
android:id="@+id/progress_bar"
style="@android:style/Widget.DeviceDefault.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_gravity="center"
android:background="@color/cardview_dark_background"
android:maxHeight="20dp"
android:minHeight="20dp" />
</RelativeLayout>
customProgress.hideProgress();
https://riptutorial.com/ 1080
Chapter 194: ProGuard - Obfuscating and
Shrinking your code
Examples
Rules for some of the widely used Libraries
1. ButterKnife
2. RxJava
3. Android Support Library
4. Android Design Support Library
5. Retrofit
6. Gson and Jackson
7. Otto
8. Crashlitycs
9. Picasso
10. Volley
11. OkHttp3
12. Parcelable
#Butterknife
-keep class butterknife.** { *; }
-keepnames class * { @butterknife.Bind *;}
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
@butterknife.* <methods>;
}
# rxjava
-keep class rx.schedulers.Schedulers {
public static <methods>;
}
-keep class rx.schedulers.ImmediateScheduler {
public <methods>;
}
-keep class rx.schedulers.TestScheduler {
public <methods>;
}
-keep class rx.schedulers.Schedulers {
public static ** test();
}
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
https://riptutorial.com/ 1081
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
long producerNode;
long consumerNode;
}
# Support library
-dontwarn android.support.**
-dontwarn android.support.v4.**
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.** { *; }
-dontwarn android.support.v7.**
-keep class android.support.v7.** { *; }
-keep interface android.support.v7.** { *; }
# support design
-dontwarn android.support.design.**
-keep class android.support.design.** { *; }
-keep interface android.support.design.** { *; }
-keep public class android.support.design.R$* { *; }
# retrofit
-dontwarn okio.**
-keepattributes Signature
-keepattributes *Annotation*
-keep class com.squareup.okhttp.** { *; }
-keep interface com.squareup.okhttp.** { *; }
-dontwarn com.squareup.okhttp.**
-dontwarn rx.**
-dontwarn retrofit.**
-keep class retrofit.** { *; }
-keepclasseswithmembers class * {
@retrofit.http.* <methods>;
}
#keep otto
-keepattributes *Annotation*
-keepclassmembers class ** {
@com.squareup.otto.Subscribe public *;
@com.squareup.otto.Produce public *;
}
# Crashlitycs 2.+
-keep class com.crashlytics.** { *; }
-keep class com.crashlytics.android.**
-keepattributes SourceFile, LineNumberTable, *Annotation*
# If you are using custom exceptions, add this line so that custom exception types are skipped
https://riptutorial.com/ 1082
during obfuscation:
-keep public class * extends java.lang.Exception
# For Fabric to properly de-obfuscate your crash reports, you need to remove this line from
your ProGuard config:
# -printmapping mapping.txt
# Picasso
-dontwarn com.squareup.okhttp.**
# Volley
-keep class com.android.volley.toolbox.ImageLoader { *; }
# OkHttp3
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
For enabling ProGuard configurations for your application you need to enable it in your module level
gradle file. you need to set the value of minifyEnabled true.
You can also enable shrinkResources true which will remove resources that ProGuard flaggs as
unused.
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
The above code will apply your ProGuard configurations contained in proguard-rules.pro
("proguard-project.txt" in Eclipse) to your released apk.
To enable you to later determine the line on which an exception occurred in a stack trace,
"proguard-rules.pro" should contain following lines:
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
If you want to remove calls to certain methods, assuming they return void and have no side affects
https://riptutorial.com/ 1083
(as in, calling them doesn't change any system values, reference arguments, statics, etc.) then
you can have ProGuard remove them from the output after the build is complete.
For example, I find this useful in removing debug/verbose logging statements useful in debugging,
but generating the strings for them is unnecessary in production.
Note: If -dontoptimize is used in any ProGuard config so that it is not minifying/removing unused
code, then this will not strip out the statements. (But who would not want to remove unused code,
right?)
Note2: this call will remove the call to log, but will not protect you code. The Strings will actually
remain in the generated apk. Read more in this post.
Obfuscation is often considered as a magic solution for code protection, by making your code
harder to understand if it ever gets de-compiled by hackers.
But if you're thinking that removing the Log.x(..) actually removes the information the hackers
need, you'll have a nasty surprise.
will indeed remove the Log call itself, but usually not the Strings you put into them.
If for example inside your log call you type a common log message such as:
Log.d(MyTag,"Score="+score);, the compiler converts the + to a 'new StringBuilder()' outside the Log
call. ProGuard doesn't change this new object.
Your de-compiled code will still have a hanging StringBuilder for "Score=", appended with the
obfuscated version for score variable (let's say it was converted to b).
Now the hacker knows what is b, and make sense of your code.
A good practice to actually remove these residuals from your code is either not put them there in
the first place (Use String formatter instead, with proguard rules to remove them), or to wrap your
Log calls with:
https://riptutorial.com/ 1084
if (BuildConfig.DEBUG) {
Log.d(TAG,".."+var);
}
Tip:
ProGuard allows the developer to obfuscate, shrink and optimize his code.
This can be done by setting the 'minifyEnabled' command to true on your desired build
#2 The second step is to specify which proguard files are we using for the given build
This can be done by setting the 'proguardFiles' line with the proper filenames
buildTypes {
debug {
minifyEnabled false
}
testRelease {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-
rules-tests.pro'
}
productionRelease {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-
rules-tests.pro', 'proguard-rules-release.pro'
}
}
#3 The developer can then edit his proguard file with the rules he desires.
That can be done by editting the file (for example 'proguard-rules-tests.pro') and adding the
desired constraints. The following file serves as an example proguard file
-verbose
https://riptutorial.com/ 1085
-dump obfuscation/class_files.txt
-printseeds obfuscation/seeds.txt
-printusage obfuscation/unused.txt // unused classes that are stripped out in the process
-printmapping obfuscation/mapping.txt // mapping file that shows the obfuscated names of the
classes after proguad is applied
// the developer can specify keywords for the obfuscation (I myself use fruits for obfuscation
names once in a while :-) )
-obfuscationdictionary obfuscation/keywords.txt
-classobfuscationdictionary obfuscation/keywords.txt
-packageobfuscationdictionary obfuscation/keywords.txt
Finally, whenever the developer runs and/or generates his new .APK file, the custom proguard
configurations will be applied thus fulfilling the requirements.
https://riptutorial.com/ 1086
Chapter 195: Project SDK versions
Introduction
An Android application needs to run on all kinds of devices. Each device may have a different
version on Android running on it.
Now, each Android version might not support all the features that your app requires, and so while
building an app, you need to keep the minimum and maximum Android version in mind.
Parameters
Parameter Details
The SDK version for each field is the Android release's SDK API level integer.
SDK
For example, Froyo (Android 2.2) corresponds to API level 8. These integers
Version
are also defined in Build.VERSION_CODES.
Remarks
There are four relevant SDK versions in every project:
• targetSdkVersion is the latest version of Android that you have tested against.
The framework will use targetSdkVersion to determine when to enable certain compatibility
behaviors. For example, targeting API level 23 or above will opt you in to the runtime
permissions model.
• minSdkVersion is the minimum version of Android that your application supports. Users
running any version of Android older than this version will not be able to install your
application or see it in the Play Store.
• maxSdkVersion is the maximum version of Android that your application supports. Users
running any version of Android newer than this version will not be able to install your
application or see it in the Play Store. This should generally not be used as most applications
will work on newer versions of Android without any additional effort.
• compileSdkVersion is the version of the Android SDK that your application will be compiled
with. It should generally be the latest version of Android that has been publicly released. This
defines which APIs you can access when writing your code. You cannot call methods
introduced in API level 23 if your compileSdkVersion is set to 22 or lower.
Examples
https://riptutorial.com/ 1087
Defining project SDK versions
In your build.gradle file of main module(app), define your minimum and target version number.
android {
//the version of sdk source used to compile your project
compileSdkVersion 23
defaultConfig {
//the minimum sdk version required by device to run your app
minSdkVersion 19
//you normally don't need to set max sdk limit so that your app can support future
versions of android without updating app
//maxSdkVersion 23
//
//the latest sdk version of android on which you are targeting(building and testing)
your app, it should be same as compileSdkVersion
targetSdkVersion 23
}
}
https://riptutorial.com/ 1088
Chapter 196: Publish .aar file to Apache
Archiva with Gradle
Examples
Simple implementation example
repositories {
mavenCentral()
}
defaultConfig {
minSdkVersion 9
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
provided 'com.android.support:support-v4:21.0.3'
provided 'com.android.support:appcompat-v7:21.0.3'
}
publishing {
publications {
repositories.maven {
url 'myurl/repositories/myrepo'
credentials {
username "user"
password "password"
}
}
https://riptutorial.com/ 1089
maven(MavenPublication) {
artifacts {
groupId 'com.mycompany'
artifactId 'mylibrary'
version '1.0'
artifact 'build/outputs/aar/app-release.aar'
}
}
}
https://riptutorial.com/ 1090
Chapter 197: Publish a library to Maven
Repositories
Examples
Publish .aar file to Maven
In order to publish to a repository in Maven format ,“maven-publish” plugin for gradle can be used.
You should define the publication and its identity attributes in build.gradle file too. This identity
attributes will be shown in the generated pom file and in future for importing this publication you
will use them.You also need to define which artifacts you want to publish,for example i just want to
publish generated .aar file after building the library.
publishing {
publications {
myPulication(MavenPublication) {
groupId 'com.example.project'
version '1.0.2'
artifactId 'myProject'
artifact("$buildDir/outputs/aar/myProject.aar")
}
}
}
publishing{
repositories {
maven {
url "http://www.myrepository.com"
}
}
}
buildscript {
...
}
android {
...
https://riptutorial.com/ 1091
}
publishing {
publications {
myPulication(MavenPublication) {
groupId 'com.example.project'
version '1.0.2'
artifactId 'myProject'
artifact("$buildDir/outputs/aar/myProject.aar")
}
}
repositories {
maven {
url "http://www.myrepository.com"
}
}
}
gradle publish
https://riptutorial.com/ 1092
Chapter 198: Publish to Play Store
Examples
Minimal app submission guide
Requirements:
• A developer account
• An apk already built and signed with a non-debug key
• A free app that doesn't have in-app billing
• no Firebase Cloud Messaging or Game Services
1. Head to https://play.google.com/apps/publish/
1a) Create your developer account if you do not have one
2. Click button Create new Application
3. Click submit APK
4. Fill in all required fields in the form, including some assets that will be displayed on the Play
Store (see image below)
5. When satisfied hit Publish app button
https://riptutorial.com/ 1093
See more about signing in Configure Signing Settings
https://riptutorial.com/ 1094
Chapter 199: Realm
Introduction
Realm Mobile Database is an alternative to SQLite. Realm Mobile Database is much faster than
an ORM, and often faster than raw SQLite.
Benefits
Offline functionality, Fast queries, Safe threading, Cross-platform apps, Encryption, Reactive
architecture.
Remarks
When you use Realm, you must remember that you mustn't pass RealmObjects, RealmResults
and Realm instances between threads. If you need a query on a given thread, open a Realm
instance on that thread. At the termination of the thread, you should close the Realm.
LEGAL NOTE: You understand that the Software may contain cryptographic functions
that may be subject to export restrictions, and you represent and warrant that you are
not located in a country that is subject to United States export restriction or embargo,
including Cuba, Iran, North Korea, Sudan, Syria or the Crimea region, and that
you are not on the Department of Commerce list of Denied Persons, Unverified Parties,
or affiliated with a Restricted Entity.
Examples
Adding Realm to your project
dependencies {
classpath "io.realm:realm-gradle-plugin:3.1.2"
}
Add the following right at the top of your app level build.gradle file.
Complete a gradle sync and you now have Realm added as a dependency to your project!
Realm requires an initial call since 2.0.0 before using it. You can do this in your Application class
or in your first Activity's onCreate method.
https://riptutorial.com/ 1095
Realm.setDefaultConfiguration(new RealmConfiguration.Builder().build());
Realm Models
Realm models must extend the RealmObject base class, they define the schema of the underlying
database.
Supported field types are boolean, byte, short, int, long, float, double, String, Date, byte[], links to
other RealmObjects, and RealmList<T extends RealmModel>.
@Ignore
private Calendar birthday; //calendars are not supported but can be ignored
// getters, setters
}
If you add (or remove) a new field to your RealmObject (or you add a new RealmObject class or
delete an existing one), a migration will be needed. You can either set deleteIfMigrationNeeded()
in your RealmConfiguration.Builder, or define the necessary migration. Migration is also required
when adding (or removing) @Required, or @Index, or @PrimaryKey annotation.
Relationships must be set manually, they are NOT automatic based on primary keys.
Since 0.88.0, it is also possible to use public fields instead of private fields/getters/setters in
RealmObject classes.
It is also possible to implement RealmModel instead of extending RealmObject, if the class is also
annotated with @RealmClass.
@RealmClass
public class Person implements RealmModel {
// ...
}
Limitations are that by a RealmObject, only RealmObject can be extended, and there is no support for
final, volatile and transient fields.
https://riptutorial.com/ 1096
It is important that a managed RealmObject class can only be modified in a transaction. A
managed RealmObject cannot be passed between threads.
Realm currently does not support storing a list of primitives. It is on their todo list (GitHub issue
#575), but for the meantime, here is a workaround.
Create a new class for your primitive type, this uses Integer, but change it for whatever you want
to store.
public RealmInteger() {
}
If you are using GSON to populate your RealmObject, you will need to add a custom type adapter.
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
})
.registerTypeAdapter(token, new TypeAdapter<RealmList<RealmInteger>>() {
@Override
public void write(JsonWriter out, RealmList<RealmInteger> value) throws
IOException {
// Empty
https://riptutorial.com/ 1097
}
@Override
public RealmList<RealmInteger> read(JsonReader in) throws IOException {
RealmList<RealmInteger> list = new RealmList<RealmInteger>();
in.beginArray();
while (in.hasNext()) {
list.add(new RealmInteger(in.nextInt()));
}
in.endArray();
return list;
}
})
.create();
try-with-resources
The Try with resources can be used only from KITKAT (minSDK 19)
Sorted queries
In order to sort a query, instead of using findAll(), you should use findAllSorted().
Note:
sort() returns a completely new RealmResults that is sorted, but an update to this RealmResults
will reset it. If you use sort(), you should always re-sort it in your RealmChangeListener, remove the
RealmChangeListener from the previous RealmResults and add it to the returned new RealmResults.
Using sort() on a RealmResults returned by an async query that is not yet loaded will fail.
will always return the results sorted by the field, even if it gets updated. It is
findAllSorted()
recommended to use findAllSorted().
Async queries
Asynchronous queries offload the evaluation of the RealmResults to another thread. In order to
https://riptutorial.com/ 1098
receive these results on the current thread, the current thread must be a looper thread (read:
async queries typically only work on the UI thread).
For queries, Realm provides the realmResults.asObservable() method. Observing results is only
possible on looper threads (typically the UI thread).
For asynchronous queries, you should filter the results by isLoaded(), so that you receive an event
only when the query has been executed. This filter() is not needed for synchronous queries (
isLoaded() always returns true on sync queries).
For writes, you should either use the executeTransactionAsync() method, or open a Realm instance
on the background thread, execute the transaction synchronously, then close the Realm instance.
https://riptutorial.com/ 1099
realmInstance.executeTransaction(realm ->
realm.insertOrUpdate(response.objects));
}
});
}
Basic Usage
Setting up an instance
To use Realm you first need to obtain an instance of it. Each Realm instance maps to a file on
disk. The most basic way to get an instance is as follows:
// Create configuration
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(context).build();
The method Realm.getInstance() creates the database file if it has not been created, otherwise
opens the file. The RealmConfiguration object controls all aspects of how a Realm is created -
whether it's an inMemory() database, name of the Realm file, if the Realm should be cleared if a
migration is needed, initial data, etc.
Please note that calls to Realm.getInstance() are reference counted (each call increments a
counter), and the counter is decremented when realm.close() is called.
Closing an instance
On background threads, it's very important to close the Realm instance(s) once it's no longer used
(for example, transaction is complete and the thread execution ends). Failure to close all Realm
instances on background thread results in version pinning, and can cause a large growth in file
size.
https://riptutorial.com/ 1100
It's worth noting that above API Level 19, you can replace this code with just this:
Models
Next step would be creating your models. Here a question might be asked, "what is a model?". A
model is a structure which defines properties of an object being stored in the database. For
example, in the following we model a book.
Note that your models should extend RealmObject class. Primary key is also specified by
@PrimaryKey annotation. Primary keys can be null, but only one element can have null as a primary
key. Also you can use the @Ignore annotation for the fields that should not be persisted to the disk:
@Ignore
https://riptutorial.com/ 1101
private String isbn;
Note that all changes to data must happen in a transaction. Another way to create an object is
using the following pattern:
https://riptutorial.com/ 1102
Deleting an object
For example, we want to delete all books by Taylor Swift:
// Start of transaction
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
// First Step: Query all Taylor Swift books
RealmResults<Book> results = ...
https://riptutorial.com/ 1103
Chapter 200: RecyclerView
Introduction
RecyclerView is a more advanced version of List View with improved performance and additional
features.
Parameters
Parameter Detail
A view previously used to display data for a specific adapter position may be
Recycle placed in a cache for later reuse to display the same type of data again later.
(view) This can drastically improve performance by skipping initial layout inflation or
construction
A child view that has entered into a temporarily detached state during layout.
Scrap Scrap views may be reused without becoming fully detached from the parent
(view) RecyclerView, either unmodified if no rebinding is required or modified by the
adapter if the view was considered dirty
Dirty (view) A child view that must be rebound by the adapter before being displayed
Remarks
The RecyclerView is a flexible view for providing a limited window into a large data set.
Before using the RecyclerView you have to add the support library dependency in the build.gradle
file:
dependencies {
// Match the version of your support library dependency
compile 'com.android.support:recyclerview-v7:25.3.1'
}
https://riptutorial.com/ 1104
You can find latest version number of recyclerview from official site.
• RecyclerView LayoutManagers
• RecyclerView ItemDecorations
• RecyclerView onClickListeners
Official Documentation
http://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
Older versions:
//it requires compileSdkVersion 25
compile 'com.android.support:recyclerview-v7:25.2.0'
compile 'com.android.support:recyclerview-v7:25.1.0'
compile 'com.android.support:recyclerview-v7:25.0.0'
Examples
Adding a RecyclerView
https://riptutorial.com/ 1105
Add the dependency as described in the Remark section, then add a RecyclerView to your layout:
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Once you have added a RecyclerView widget to your layout, obtain a handle to the object, connect
it to a layout manager and attach an adapter for the data to be displayed:
// specify an adapter
mAdapter = new MyAdapter(myDataset);
mRecyclerView.setAdapter(mAdapter);
xmlns:app="http://schemas.android.com/apk/res-auto"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
If you know that changes in content of the RecyclerView won't change the layout size of the
RecyclerView, use the following code to improve the performance of the component. If
RecyclerView has a fixed size, it knows that RecyclerView itself will not resize due to its children,
so it doesn’t call request layout at all. It just handles the change itself. If invalidating whatever the
parent is, the coordinator, layout, or whatever. (you can use this method even before setting
LayoutManager and Adapter):
mRecyclerView.setHasFixedSize(true);
RecyclerViewprovides these built-in layout managers to use. So you can create a list, a grid and a
staggered grid using RecyclerView:
If the items in your RecyclerView load data from the network (commonly images) or carry out other
processing, that can take a significant amount of time and you may end up with items on-screen
but not fully loaded. To avoid this you can extend the existing LinearLayoutManager to preload a
number of items before they become visible on-screen:
package com.example;
https://riptutorial.com/ 1106
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.RecyclerView;
/**
* A LinearLayoutManager that preloads items off-screen.
* <p>
* Preloading is useful in situations where items might take some time to load
* fully, commonly because they have maps, images or other items that require
* network requests to complete before they can be displayed.
* <p>
* By default, this layout will load a single additional page's worth of items,
* a page being a pixel measure equivalent to the on-screen size of the
* recycler view. This can be altered using the relevant constructor, or
* through the {@link #setPages(int)} method.
*/
public class PreLoadingLinearLayoutManager extends LinearLayoutManager {
private int mPages = 1;
private OrientationHelper mOrientationHelper;
@Override
public void setOrientation(final int orientation) {
super.setOrientation(orientation);
mOrientationHelper = null;
}
/**
* Set the number of pages of layout that will be preloaded off-screen,
* a page being a pixel measure equivalent to the on-screen size of the
* recycler view.
* @param pages the number of pages; can be {@code 0} to disable preloading
*/
public void setPages(final int pages) {
this.mPages = pages;
}
@Override
protected int getExtraLayoutSpace(final RecyclerView.State state) {
if (mOrientationHelper == null) {
mOrientationHelper = OrientationHelper.createOrientationHelper(this, getOrientation());
}
return mOrientationHelper.getTotalSpace() * mPages;
}
}
https://riptutorial.com/ 1107
Drag&Drop and Swipe with RecyclerView
You can implement the swipe-to-dismiss and drag-and-drop features with the RecyclerView
without using 3rd party libraries.
Just use the ItemTouchHelper class included in the RecyclerView support library.
Instantiate the ItemTouchHelper with the SimpleCallback callback and depending on which
functionality you support, you should override onMove(RecyclerView, ViewHolder, ViewHolder) and /
or onSwiped(ViewHolder, int)and and finally attach to your RecyclerView.
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
// remove item from adapter
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
final int fromPos = viewHolder.getAdapterPosition();
final int toPos = target.getAdapterPosition();
// move item in `fromPos` to `toPos` in adapter.
return true;// true if moved, false otherwise
}
};
It's worth mentioning that SimpleCallback constructor applies the same swiping strategy to all items
in the RecyclerView. It's possible in any case to update the default swiping direction for specific
items by simply overriding method getSwipeDirs(RecyclerView, ViewHolder).
Let's suppose for example that our RecyclerView includes a HeaderViewHolder and that we obviously
don't want to apply swiping to it. It will be enough to override getSwipeDirs as follows:
@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
if (viewHolder instanceof HeaderViewHolder) {
// no swipe for header
return 0;
}
// default swipe for all other items
return super.getSwipeDirs(recyclerView, viewHolder);
}
https://riptutorial.com/ 1108
public class SampleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Do whatever you want on clicking the normal items
}
});
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
if (viewType == FOOTER_VIEW) {
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_footer,
parent, false);
return vh;
}
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_normal, parent,
false);
return vh;
}
https://riptutorial.com/ 1109
try {
if (holder instanceof NormalViewHolder) {
NormalViewHolder vh = (NormalViewHolder) holder;
vh.bindView(position);
} else if (holder instanceof FooterViewHolder) {
FooterViewHolder vh = (FooterViewHolder) holder;
}
} catch (Exception e) {
e.printStackTrace();
}
}
// Now the critical part. You have return the exact item count of your list
// I've only one footer. So I returned data.size() + 1
// If you've multiple headers and footers, you've to return total count
// like, headers.size() + data.size() + footers.size()
@Override
public int getItemCount() {
if (data == null) {
return 0;
}
if (data.size() == 0) {
//Return 1 here to show nothing
return 1;
}
@Override
public int getItemViewType(int position) {
if (position == data.size()) {
// This is where we'll add footer.
return FOOTER_VIEW;
}
return super.getItemViewType(position);
}
https://riptutorial.com/ 1110
Here's a good read about the implementation of RecyclerView with header and footer.
Alternate method:
While the above answer will work you can use this approach as well using a recycler view using a
NestedScrollView .You can add a layout for header using the following approach:
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
layout="@layout/drawer_view_header"
android:id="@+id/navigation_header"/>
<android.support.v7.widget.RecyclerView
android:layout_below="@id/navigation_header"
android:id="@+id/followers_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
</android.support.v4.widget.NestedScrollView>
Or you may also use a LinearLayout with vertical alignment in your NestedScrollView.
compile 'com.android.support:recyclerview-v7:23.2.0'
Sometimes a RecyclerView will need to use several types of Views to be displayed in the list
shown in the UI, and each View needs a different layout xml to be inflated.
For this issue, you may use different ViewHolders in single Adapter, by using a special method in
RecyclerView - getItemViewType(int position).
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(context).inflate(viewType, parent, false);
return ViewHolder.create(itemView, viewType);
}
https://riptutorial.com/ 1111
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final Item model = this.items.get(position);
((ViewHolder) holder).bind(model);
}
@Override
public int getItemViewType(int position) {
return inSearchState ? R.layout.item_header : R.layout.item_entry;
}
public EntryViewHolder(View v) {
this.v = v;
}
public HeaderViewHolder(View v) {
this.v = v;
}
https://riptutorial.com/ 1112
result.add(item);
}
}
items.clear();
items.addAll(result);
}
notifyDataSetChanged();
}
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
adapter.filter(query);
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
adapter.filter(newText);
return true;
}
});
note: In this code I am using btnExpand click-event, for whole recyclerview click event you can set
listener to itemView object.
cv = (CardView)itemView.findViewById(R.id.cardview);
recordName = (TextView)itemView.findViewById(R.id.tv_record);
visibleFile = (TextView)itemView.findViewById(R.id.visible_file);
date = (TextView)itemView.findViewById(R.id.date);
time = (TextView)itemView.findViewById(R.id.time);
btnIn = (Button)itemView.findViewById(R.id.btn_in_out);
btnExpand.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PopupMenu popup = new PopupMenu(btnExpand.getContext(), itemView);
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
https://riptutorial.com/ 1113
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_delete:
moveFile(recordName.getText().toString(),
getAdapterPosition());
return true;
case R.id.action_play:
String valueOfPath = recordName.getText().toString();
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
File file = new File(valueOfPath);
intent.setDataAndType(Uri.fromFile(file), "audio/*");
context.startActivity(intent);
return true;
case R.id.action_share:
String valueOfPath = recordName.getText().toString();
File filee = new File(valueOfPath);
try {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.setType("audio/*");
sendIntent.putExtra(Intent.EXTRA_STREAM,
Uri.fromFile(filee));
context.startActivity(sendIntent);
} catch (NoSuchMethodError | IllegalArgumentException |
NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return true;
default:
return false;
}
}
});
// here you can inflate your menu
popup.inflate(R.menu.my_menu_item);
popup.setGravity(Gravity.RIGHT);
// if you want icon with menu items then write this try-catch block.
try {
Field mFieldPopup=popup.getClass().getDeclaredField("mPopup");
mFieldPopup.setAccessible(true);
MenuPopupHelper mPopup = (MenuPopupHelper) mFieldPopup.get(popup);
mPopup.setForceShowIcon(true);
} catch (Exception e) {
}
popup.show();
}
});
}
}
try {
https://riptutorial.com/ 1114
Field[] fields = popup.getClass().getDeclaredFields();
for (Field field : fields) {
if ("mPopup".equals(field.getName())) {
field.setAccessible(true);
Object menuPopupHelper = field.get(popup);
Class<?> classPopupHelper = Class.forName(menuPopupHelper
.getClass().getName());
Method setForceIcons = classPopupHelper.getMethod(
"setForceShowIcon", boolean.class);
setForceIcons.invoke(menuPopupHelper, true);
break;
}
}
} catch (Exception e) {
RecyclerView will perform a relevant animation if any of the "notify" methods are used except for
notifyDataSetChanged; this includes notifyItemChanged, notifyItemInserted, notifyItemMoved,
notifyItemRemoved, etc.
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import java.util.List;
https://riptutorial.com/ 1115
applyAndAnimateAdditions(models);
applyAndAnimateMovedItems(models);
}
You should NOT use the same List for setModels and List in the adapter.
https://riptutorial.com/ 1116
private List<DataModel> models;
private YourAdapter adapter;
adapter.setModels(new ArrayList(models));
import android.support.v7.util.SortedList;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.util.SortedListAdapterCallback;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
TextView text;
CheckBox checkBox;
ViewHolder(View itemView){
super(itemView);
https://riptutorial.com/ 1117
checkBox.setChecked(model.isChecked());
}
}
public MyAdapter() {
mSortedList = new SortedList<>(DataModel.class, new
SortedListAdapterCallback<DataModel>(this) {
@Override
public int compare(DataModel o1, DataModel o2) {
//This gets called to find the ordering between objects in the array.
if (o1.someValue() < o2.someValue()) {
return -1;
} else if (o1.someValue() > o2.someValue()) {
return 1;
} else {
return 0;
}
}
@Override
public boolean areContentsTheSame(DataModel oldItem, DataModel newItem) {
//This is to see of the content of this object has changed. These items are
only considered equal if areItemsTheSame() returned true.
@Override
public boolean areItemsTheSame(DataModel item1, DataModel item2) {
//Checks to see if these two items are the same. If not, it is added to the
list, otherwise, check if content has changed.
return item1.equals(item2);
}
});
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = //Initiate your item view here.
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
//Just update the holder with the object in the sorted list from the given position
DataModel model = mSortedList.get(position);
if (model != null) {
holder.setDataModel(model);
}
}
@Override
public int getItemCount() {
return mSortedList.size();
}
https://riptutorial.com/ 1118
animation.
mSortedList.beginBatchedUpdates();
mSortedList.clear();
mSortedList.addAll(models);
mSortedList.endBatchedUpdates();
}
//The following methods each modify the data set and automatically handles calling the
appropriate 'notify' method on the adapter.
public void addModel(DataModel model) {
mSortedList.add(model);
}
Here is a generic ViewHolder class that you can use with any DataBinding layout. Here an
instance of particular ViewDataBinding class is created using the inflated View object and
DataBindingUtil utility class.
import android.databinding.DataBindingUtil;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public T getBinding() {
return binding;
}
}
After creating this class you can use the <layout> in your layout file to enable databinding for that
layout like this:
https://riptutorial.com/ 1119
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="item"
type="ItemModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@{item.itemLabel}" />
</LinearLayout>
</layout>
By default, Android Data Binding library generates a ViewDataBinding class based on the layout file
name, converting it to Pascal case and suffixing "Binding" to it. For this example it would be
MyItemBinding for the layout file my_item.xml. That Binding class would also have a setter method to
set the object defined as data in the layout file(ItemModel for this example).
Now that we have all the pieces we can implement our adapter like this:
https://riptutorial.com/ 1120
Endless Scrolling in Recycleview.
Here I have shared a code snippet for implementing endless scrolling in recycle view.
Step 1: First make a one abstract method in Recycleview adapter like below.
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if ((position >= getItemCount() - 1)) {
load();
}
}
@Override
public int getItemCount() {
return YOURLIST.size();
}
Step 3: Now every backend logic is complete now it's time to execute this logic.It's simple you can
override load method where you create object of your adapter.this method is automatically call
while user reach at end of the listing.
Now load() method automatically call while user scroll at end of list.
Best Luck
Show default view till items load or when data is not available
Screenshot
https://riptutorial.com/ 1121
Adapter Class
MyAdapter() {
super();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == EMPTY_VIEW) {
return new EmptyView(layoutInflater.inflate(R.layout.nothing_yet, parent, false));
} else {
return new ItemView(layoutInflater.inflate(R.layout.my_item, parent, false));
}
}
@SuppressLint("SetTextI18n")
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == EMPTY_VIEW) {
EmptyView emptyView = (EmptyView) holder;
https://riptutorial.com/ 1122
emptyView.primaryText.setText("No data yet");
emptyView.secondaryText.setText("You're doing good !");
emptyView.primaryText.setCompoundDrawablesWithIntrinsicBounds(null, new
IconicsDrawable(getActivity()).icon(FontAwesome.Icon.faw_ticket).sizeDp(48).color(Color.DKGRAY),
null, null);
} else {
ItemView itemView = (ItemView) holder;
// Bind data to itemView
}
}
@Override
public int getItemCount() {
return datalist.size() > 0 ? datalist.size() : 1;
}
@Override
public int getItemViewType(int position) {
if datalist.size() == 0) {
return EMPTY_VIEW;
}
return super.getItemViewType(position);
}
nothing_yet.xml
<TextView
android:id="@+id/nothingPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawableTint="@android:color/secondary_text_light"
android:drawableTop="@drawable/ic_folder_open_black_24dp"
android:enabled="false"
android:fontFamily="sans-serif-light"
android:text="No Item's Yet"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="@android:color/secondary_text_light"
android:textSize="40sp"
tools:targetApi="m" />
<TextView
android:id="@+id/nothingSecondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:enabled="false"
https://riptutorial.com/ 1123
android:fontFamily="sans-serif-condensed"
android:text="You're doing good !"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/tertiary_text_light" />
</LinearLayout>
I'm using FontAwesome with Iconics Library for the images. Add this to your app level build.gradle
file.
compile 'com.mikepenz:fontawesome-typeface:4.6.0.3@aar'
compile 'com.mikepenz:iconics-core:2.8.1@aar'
compile "com.android.support:appcompat-v7:25.3.1"
compile "com.android.support:recyclerview-v7:25.3.1"
https://riptutorial.com/ 1124
// Set the drawable on it
hItemDecoration.setDrawable(mDivider);
https://riptutorial.com/ 1125
Chapter 201: RecyclerView and
LayoutManagers
Examples
GridLayoutManager with dynamic span count
When creating a recyclerview with a gridlayout layout manager you have to specify the span count
in the constructor. Span count refers to the number of columns. This is fairly clunky and doesn't
take into account larger screen sizes or screen orientation. One approach is to create multiple
layouts for the various screen sizes. Another more dynamic approach can be seen below.
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec, heightSpec);
if (columnWidth > 0) {
int spanCount = Math.max(1, getMeasuredWidth() / columnWidth);
manager.setSpanCount(spanCount);
https://riptutorial.com/ 1126
}
}
}
This class determines how many columns can fit into the recyclerview. To use it you will need to
put it into your layout.xml as follows:
Notice that we use the columnWidth attribute. The recyclerview will need it to determine how many
columns will fit into the available space.
In your activity/fragment you just get a reference to the recylerview and set an adapter to it (and
any item decorations or animations that you want to add). DO NOT SET A LAYOUT MANAGER
You now have a recyclerview that will adjust the spancount (ie columns) to fit the screen size. As a
final addition you might want to center the columns in the recyclerview (by default they are aligned
to layout_start). You can do that by modifying the AutofitRecyclerView class a little. Start by
creating an inner class in the recyclerview. This will be a class that extends from
GridLayoutManager. It will add enough padding to the left and right in order to center the rows:
https://riptutorial.com/ 1127
@Override
public int getPaddingLeft() {
final int totalItemWidth = columnWidth * getSpanCount();
if (totalItemWidth >= AutofitRecyclerView.this.getMeasuredWidth()) {
return super.getPaddingLeft(); // do nothing
} else {
return Math.round((AutofitRecyclerView.this.getMeasuredWidth() / (1f +
getSpanCount())) - (totalItemWidth / (1f + getSpanCount())));
}
}
@Override
public int getPaddingRight() {
return getPaddingLeft();
}
}
}
Then when you set the LayoutManager in the AutofitRecyclerView use the
CenteredGridLayoutManager as follows:
And that's it! You have a dynamic spancount, center aligned gridlayoutmanager based
recyclerview.
Sources:
To add a header to a recyclerview with a gridlayout, first the adapter needs to be told that the
header view is the first position - rather than the standard cell used for the content. Next, the
layout manager must be told that the first position should have a span equal to the *span count of
the entire list. *
https://riptutorial.com/ 1128
private static final int ITEM_VIEW_TYPE_HEADER = 0;
private static final int ITEM_VIEW_TYPE_ITEM = 1;
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (viewType == ITEM_VIEW_TYPE_HEADER) {
View headerView = inflater.inflate(R.layout.header, parent, false);
return new HeaderHolder(headerView);
}
@Override
public int getItemViewType(int position) {
return isHeader(position) ? ITEM_VIEW_TYPE_HEADER : ITEM_VIEW_TYPE_ITEM;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder h, int position) {
if (isHeader(position)) {
return;
}
@Override
public int getItemCount() {
return _categories.size() + 1; // add one for the header
}
}
https://riptutorial.com/ 1129
mRecyclerView.setAdapter(adapter);
The same approach can be used add a footer in addition to or instead of a header.
This example adds a list of places with image and name by using an ArrayList of custom Place
objects as dataset.
Activity layout
The layout of the activity / fragment or where the RecyclerView is used only has to contain the
RecyclerView. There is no ScrollView or a specific layout needed.
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
When a list item refers to different data types like text, numbers, images (as in this example with
places), it is often a good idea to use a custom object.
// typical constructor
public Place(Bitmap image, String name) {
this.image = image;
this.name = name;
}
// getters
public Bitmap getImage() {
return image;
https://riptutorial.com/ 1130
}
public String getName() {
return name;
}
}
<ImageView
android:id="@+id/image"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
First, we implement the ViewHolder. It only inherits the default constructor and saves the needed
views into some fields:
https://riptutorial.com/ 1131
public class ViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private TextView nameView;
// ...
}
To use our custom list item layout, we override the method onCreateViewHolder(...). In this
example, the layout file is called place_list_item.xml.
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.place_list_item,
parent,
false
);
return new ViewHolder(view);
}
// ...
}
In the onBindViewHolder(...), we actually set the views' contents. We get the used model by finding
it in the List at the given position and then set image and name on the ViewHolder's views.
@Override
public void onBindViewHolder(PlaceListAdapter.ViewHolder viewHolder, int position) {
Place place = mPlaces.get(position);
viewHolder.nameView.setText(place.getName());
viewHolder.imageView.setImageBitmap(place.getImage());
}
https://riptutorial.com/ 1132
// ...
}
We also need to implement getItemCount(), which simply return the List's size.
@Override
public int getItemCount() {
return mPlaces.size();
}
// ...
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
// ...
}
@Override
https://riptutorial.com/ 1133
protected void onCreate(Bundle savedInstanceState) {
// ...
Done!
StaggeredGridLayoutManager
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:id="@+id/imageView"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:id="@+id/name"
android:layout_gravity="center"
android:textColor="@android:color/white"/>
https://riptutorial.com/ 1134
public class PintrestAdapter extends
RecyclerView.Adapter<PintrestAdapter.PintrestViewHolder>{
private ArrayList<PintrestItem>images;
Picasso picasso;
Context context;
public PintrestAdapter(ArrayList<PintrestItem>images,Context context){
this.images=images;
picasso=Picasso.with(context);
this.context=context;
@Override
public PintrestViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view=
LayoutInflater.from(parent.getContext()).inflate(R.layout.pintrest_layout_item,parent,false);
@Override
public void onBindViewHolder(PintrestViewHolder holder, int position) {
picasso.load(images.get(position).getUrl()).into(holder.imageView);
holder.tv.setText(images.get(position).getName());
}
@Override
public int getItemCount() {
return images.size();
}
}
}
}
https://riptutorial.com/ 1135
compile 'com.squareup.picasso:picasso:2.5.2'
https://riptutorial.com/ 1136
Chapter 202: RecyclerView Decorations
Syntax
• RecyclerView addItemDecoration(RecyclerView.ItemDecoration decoration)
• RecyclerView addItemDecoration(RecyclerView.ItemDecoration decoration, int index)
Parameters
Parameter Details
the index in the list of decorations for this RecyclerView. This is the order in which
index
getItemOffset and onDraw are called. Later calls might overdraw previous ones.
Remarks
Multiple decorations
Adding multiple decorations to a RecyclerView will work in some cases, but there is currently no
public API to take other possible decorations into account when measuring or drawing. You can
get the view bounds or the view decorated bounds, where the decorated bounds are the sum of all
the decoration offsets applied.
Official javadoc
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ItemDecoration.html
Examples
https://riptutorial.com/ 1137
Drawing a Separator
This will draw a line at the bottom of every view but the last to act as a separator between items.
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
// add space for the separator to the bottom of every view but the last one
if (position < state.getItemCount()) {
outRect.set(0, 0, 0, (int) mPaint.getStrokeWidth()); // left, top, right, bottom
} else {
outRect.setEmpty(); // 0, 0, 0, 0
}
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
// a line will draw half its size to top and bottom,
// hence the offset to place it correctly
final int offset = (int) (mPaint.getStrokeWidth() / 2);
https://riptutorial.com/ 1138
}
}
}
}
You can use a RecyclerView.ItemDecoration to put extra margins around each item in a
RecyclerView. This can in some cases clean up both your adapter implementation and your item
view XML.
@Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
// in your onCreate()
RecyclerView rv = (RecyclerView) findItemById(R.id.myList);
rv.addItemDecoration(new MyItemDecoration(context));
https://riptutorial.com/ 1139
First of all you need to create a class which extends RecyclerView.ItemDecoration :
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
//divider padding give some padding whatever u want or disable
int left =parent.getPaddingLeft()+80;
int right = parent.getWidth() - parent.getPaddingRight()-30;
recyclerView.addItemDecoration(new SimpleBlueDivider(context));
https://riptutorial.com/ 1140
https://riptutorial.com/ 1141
This image is just an example how dividers working , if you want to follow Material Design specs
when adding dividers please take a look at this link : dividers and thanks @Brenden Kromhout by
providing link .
ItemOffsetDecoration.java
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (position % 2 != 0) {
outRect.right = mItemOffset;
}
outRect.left = mItemOffset;
outRect.bottom = mItemOffset;
}
https://riptutorial.com/ 1142
}
recyclerView.setLayoutManager(lLayout);
<dimen name="item_offset">5dp</dimen>
https://riptutorial.com/ 1143
Chapter 203: RecyclerView onClickListeners
Examples
New Example
/**
* Provide a reference to the type of views that you are using (custom ViewHolder)
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView textView;
public ViewHolder(View v) {
super(v);
// Define click listener for the ViewHolder's View.
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { // handle click events here
Log.d(TAG, "Element " + getPosition() + " clicked.");
mListener.onRVItemClicked(getPosition(),v); //set callback
}
});
textView = (TextView) v.findViewById(R.id.textView);
}
/**
* Initialize the dataset of the Adapter.
*
* @param dataSet String[] containing the data to populate views to be used by
RecyclerView.
*/
public SampleAdapter(String[] dataSet) {
mDataSet = dataSet;
}
https://riptutorial.com/ 1144
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
// Get element from your dataset at this position and replace the contents of the view
// with that element
viewHolder.getTextView().setText(mDataSet[position]);
}
First example reimplemented in Kotlin and using RxJava for cleaner interaction.
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.support.v7.widget.RecyclerView
import rx.subjects.PublishSubject
init {
https://riptutorial.com/ 1145
view.setOnClickListener { v -> itemClickStream.onNext(v) }
}
Usage is quite simple then. It's possible to subscribe on separate thread using RxJava facilities.
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
@Override
public void onClick(View v) {
onclicklistner.onItemClick(getAdapterPosition(), v);
}
@Override
public boolean onLongClick(View v) {
onclicklistner.onItemLongClick(getAdapterPosition(), v);
return true;
}
https://riptutorial.com/ 1146
void onItemLongClick(int position, View v);
}
Adaptor demo
package adaptor;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.wings.example.recycleview.MainActivity;
import com.wings.example.recycleview.R;
import java.util.ArrayList;
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
itemView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
https://riptutorial.com/ 1147
onclicklistner.onItemClick(getAdapterPosition(), v);
}
@Override
public boolean onLongClick(View v) {
onclicklistner.onItemLongClick(getAdapterPosition(), v);
return true;
}
}
@Override
public int getItemCount() {
return arrayList.size()+1;
}
@Override
public int getItemViewType(int position) {
return position == 0 ? VIEW_HEADER : VIEW_NORMAL;
}
@SuppressLint("InflateParams")
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
if (viewType == VIEW_HEADER) {
return new HeaderViewHolder(headerView);
} else {
View view =
LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.custom_recycler_row_sample_item,
viewGroup, false);
return new ItemViewHolder(view, this);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
if (viewHolder.getItemViewType() == VIEW_HEADER) {
return;
} else {
ItemViewHolder itemViewHolder = (ItemViewHolder) viewHolder;
itemViewHolder.txt_pos.setText(arrayList.get(position-1));
}
}
}
sampleAdapter.setOnItemClickListener(new SampleAdapter.onClickListner() {
https://riptutorial.com/ 1148
@Override
public void onItemClick(int position, View v) {
position = position+1;//As we are adding header
Log.e(TAG + "ON ITEM CLICK", position + "");
Snackbar.make(v, "On item click "+position, Snackbar.LENGTH_LONG).show();
}
@Override
public void onItemLongClick(int position, View v) {
position = position+1;//As we are adding header
Log.e(TAG + "ON ITEM LONG CLICK", position + "");
Snackbar.make(v, "On item longclick "+position, Snackbar.LENGTH_LONG).show();
}
});
To implement an item click listener and/or an item long click listener, you can create an interface in
your adapter:
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mOnItemClickListener != null) {
mOnItemClickListener.onItemSeleted(position, view,
mDataSet.get(position));
}
}
});
itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if(mOnItemLongClickListener != null) {
return mOnItemLongClickListener.onItemSelected(position, view,
mDataSet.get(position));
}
}
});
https://riptutorial.com/ 1149
}
@Override
public CustomAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.view_item_custom, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(CustomAdapter.ViewHolder holder, int position) {
// Bind views
}
@Override
public int getItemCount() {
return mDataSet.size();
}
Then you can set your click listeners after you create an instance of the adapter:
customAdapter.setOnItemClickListener(new CustomAdapter.OnItemClickListener {
@Override
public void onItemSelected(int position, View view, CustomObject object) {
// Your implementation here
}
});
customAdapter.setOnItemLongClickListener(new CustomAdapter.OnItemLongClickListener {
@Override
public boolean onItemSelected(int position, View view, CustomObject object) {
// Your implementation here
return true;
}
});
Another way to implement item click listener is to use interface with several methods, the number
https://riptutorial.com/ 1150
of which is equal to the number of clickable views, and use overrided click listeners as you can
see below. This method is more flexible, because you can set click listeners to different views and
quite easy control the click logic separately for each.
public CustomAdapter(){
mList = new ArrayList<>();
}
@Override
public CustomHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
return new CustomHolder(v);
}
@Override
public void onBindViewHolder(CustomHolder holder, int position) {
//make all even positions not clickable
holder.firstClickListener.setClickable(position%2==0);
holder.firstClickListener.setPosition(position);
holder.secondClickListener.setPosition(position);
}
@Override
public void onClick(View v) {
if(mClickable) {
mClickInterface.clickEventOne(mObjects.get(mPosition));
https://riptutorial.com/ 1151
}
}
}
@Override
public void onClick(View v) {
mClickInterface.clickEventTwo(mObjects.get(mPosition), v);
}
}
@Override
public int getItemCount() {
return mObjects.size();
}
v1.setOnClickListener(firstClickListener);
v2.setOnClickListener(secondClickListener);
}
}
}
And when you have an instance of adapter, you can set your click listener which listens to clicking
on each of the views:
customAdapter.setClickInterface(new CustomAdapter.ClickInterface {
@Override
public void clickEventOne(Object obj) {
// Your implementation here
}
@Override
public void clickEventTwo(Object obj1, Object obj2) {
// Your implementation here
}
});
https://riptutorial.com/ 1152
private GestureDetector gestureDetector;
private RecyclerTouchListener.ClickListener clickListener;
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
clickListener.onClick(child, rv.getChildPosition(child));
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
In MainActivity
https://riptutorial.com/ 1153
@Override
public void onClick(View child, int childPosition) {
}
}));
https://riptutorial.com/ 1154
Chapter 204: RenderScript
Introduction
RenderScript is a scripting language that allows you to write high performance graphic rendering
and raw computational code. It provides a means of writing performance critical code that the
system later compiles to native code for the processor it can run on. This could be the CPU, a
multi-core CPU, or even the GPU. Which it ultimately runs on depends on many factors that aren't
readily available to the developer, but also depends on what architecture the internal platform
compiler supports.
Examples
Getting Started
Scripts are written in a C99 based language (C99 being an old version of the C programming
language standard). For each Script a Java class is created which allows you to easily interact
with RenderScript in your Java code.
android {
compileSdkVersion 24
buildToolsVersion '24.0.1'
defaultConfig {
minSdkVersion 8
targetSdkVersion 24
renderscriptTargetApi 18
renderscriptSupportModeEnabled true
}
}
• renderscriptTargetApi
https://riptutorial.com/ 1155
: This should be set to the version earliest API level which provides all RenderScript
functionality you require.
• renderscriptSupportModeEnabled: This enables the use of the Support Library RenderScript
implementation.
A Kernel is a function which is executed against every element inside an Allocation. An Allocation
can be used to pass data like a Bitmap or a byte array to a RenderScript and they are also used to
get a result from a Kernel. Kernels can either take one Allocation as input and another as output
or they can modify the data inside just one Allocation.
You can write your one Kernels, but there are also many predefined Kernels which you can use to
perform common operations like a Gaussian Image Blur.
As already mentioned for every RenderScript file a class is generated to interact with it. These
classes always start with the prefix ScriptC_ followed by the name of the RenderScript file. For
example if your RenderScript file is called example then the generated Java class will be called
ScriptC_example. All predefined Scripts just start with the prefix Script - for example the Gaussian
Image Blur Script is called ScriptIntrinsicBlur.
https://riptutorial.com/ 1156
RenderScript Boilerplate
RenderScript files reside in the folder src/main/rs in your project. Each file has the file extension
.rs and has to contain two #pragma statements at the top:
#pragma version(1)
#pragma rs java_package_name(your.package.name)
• #pragma version(1): This can be used to set the version of RenderScript you are using.
Currently there is only version 1.
There is another #pragma you should usually set in each of your RenderScript files and it is used to
set the floating point precision. You can set the floating point precision to three different levels:
• #pragma rs_fp_full:This is the strictest setting with the highest precision and it is also the
default value if don't specify anything. You should use this if you require high floating point
precision.
• #pragma rs_fp_relaxed: This is ensures not quite as high floating point precision, but on some
architectures it enables a bunch of optimizations which can cause your scripts to run faster.
• #pragma rs_fp_imprecise: This ensures even less precision and should be used if floating
point precision does not really matter to your script.
https://riptutorial.com/ 1157
Most scripts can just use #pragma rs_fp_relaxed unless you really need high floating point precision.
Global Variables
Now just like in C code you can define global variables or constants:
The variable gMonoMult is of type float3. This means it is a vector consisting of 3 float numbers.
The other float variable called saturationValue is not constant, therefore you can set it at runtime
to a value you like. You can use variables like this in your Kernels or functions and therefore they
are another way to give input to or receive output from your RenderScripts. For each not constant
variable a getter and setter method will be generated on the associated Java class.
Kernels
But now lets get started implementing the Kernel. For the purposes of this example I am not going
to explain the math used in the Kernel to modify the saturation of the image, but instead will focus
on how to implement a Kernel and and how to use it. At the end of this chapter I will quickly
explain what the code in this Kernel is actually doing.
Kernels in general
As you can see it looks like a normal C function with one exception: The __attribute__((kernel))
between the return type and method name. This is what tells RenderScript that this method is a
Kernel. Another thing you might notice is that this method accepts a uchar4 parameter and returns
another uchar4 value. uchar4 is - like the float3 variable we discussed in the chapter before - a
vector. It contains 4 uchar values which are just byte values in the range from 0 to 255.
You can access these individual values in many different ways, for example in.r would return the
byte which corresponds to the red channel of a pixel. We use a uchar4 since each pixel is made up
of 4 values - r for red, g for green, b for blue and a for alpha - and you can access them with this
shorthand. RenderScript also allows you to take any number of values from a vector and create
another vector with them. For example in.rgb would return a uchar3 value which just contains the
red, green and blue parts of the pixel without the alpha value.
At runtime RenderScript will call this Kernel method for each pixel of an image which is why the
https://riptutorial.com/ 1158
return value and parameter are just one uchar4 value. RenderScript will run many of these calls in
parallel on all available processors which is why RenderScript is so powerful. This also means that
you don't have to worry about threading or thread safety, you can just implement whatever you
want to do to each pixel and RenderScript takes care of the rest.
When calling a Kernel in Java you supply two Allocation variables, one which contains the input
data and another one which will receive the output. Your Kernel method will be called for each
value in the input Allocation and will write the result to the output Allocation.
In the Kernel above a few methods are used which are provided out of the box. RenderScript
provides many such methods and they are vital for almost anything you are going to do with
RenderScript. Among them are methods to do math operations like sin() and helper methods like
mix() which mixes two values according to another values. But there are also methods for more
complex operations when dealing with vectors, quaternions and matrices.
The official RenderScript Runtime API Reference is the best resource out there if you want to
know more about a particular method or are looking for a specific method which performs a
common operation like calculating the dot product of a matrix. You can find this documentation
here.
Kernel Implementation
Now let's take a look at the specifics of what this Kernel is doing. Here's the first line in the Kernel:
float4 f4 = rsUnpackColor8888(in);
The first line calls the built in method rsUnpackColor8888() which transforms the uchar4 value to a
float4 values. Each color channel is also transformed to the range 0.0f - 1.0f where 0.0f
corresponds to a byte value of 0 and 1.0f to 255. The main purpose of this is to make all the math
in this Kernel a lot simpler.
This next line uses the built in method dot() to calculate the dot product of two vectors. gMonoMult
is a constant value we defined a few chapters above. Since both vectors need to be of the same
length to calculate the dot product and also since we just want to affect the color channels and not
the alpha channel of a pixel we use the shorthand .rgb to get a new float3 vector which just
contains the red, green and blue color channels. Those of us who still remember from school how
the dot product works will quickly notice that the dot product should return just one value and not a
vector. Yet in the code above we are assigning the result to a float3 vector. This is again a feature
of RenderScript. When you assign a one dimensional number to a vector all elements in the vector
will be set to this value. For example the following snippet will assign 2.0f to each of the three
values in the float3 vector:
https://riptutorial.com/ 1159
So the result of the dot product above is assigned to each element in the float3 vector above.
Now comes the part in which we actually use the global variable saturationLevel to modify the
saturation of the image:
This uses the built in method mix() to mix together the original color with the dot product vector we
created above. How they are mixed together is determined by the global saturationLevel variable.
So a saturationLevel of 0.0f will cause the resulting color to have no part of the original color
values and will only consist of values in the dotVector which results in a black and white or grayed
out image. A value of 1.0f will cause the resulting color to be completely made up of the original
color values and values above 1.0f will multiply the original colors to make them more bright and
intense.
return rsPackColorTo8888(newColor);
This is the last part in the Kernel. rsPackColorTo8888() transforms the float3 vector back to a uchar4
value which is then returned. The resulting byte values are clamped to a range between 0 and
255, so float values higher than 1.0f will result in a byte value of 255 and values lower than 0.0 will
result in a byte value of 0.
And that is the whole Kernel implementation. Now there is only one part remaining: How to call a
Kernel in Java.
The static method create() can be used to create a RenderScript instance from a Context. You can
then instantiate the Java class which was generated for your script. If you called the RenderScript
file saturation.rs then the class will be called ScriptC_saturation:
On this class you can now set the saturation level and call the Kernel. The setter which was
generated for the saturationLevel variable will have the prefix set_ followed by the name of the
variable:
https://riptutorial.com/ 1160
script.set_saturationLevel(1.0f);
There is also a getter prefixed with get_ which allows you to get the saturation level currently set:
Kernels you define in your RenderScript are prefixed with forEach_ followed by the name of the
Kernel method. The Kernel we have written expects an input Allocation and an output Allocation
as its parameters:
script.forEach_saturation(inputAllocation, outputAllocation);
The input Allocation needs to contain the input image, and after the forEach_saturation method
has finished the output allocation will contain the modified image data.
Once you have an Allocation instance you can copy data from and to those Allocations by using
the methods copyFrom() and copyTo(). For example you can copy a new image into your input
`Allocation by calling:
inputAllocation.copyFrom(inputBitmap);
The same way you can retrieve the result image by calling copyTo() on the output Allocation:
outputAllocation.copyTo(outputBitmap);
We can use the static method createFromBitmap() to quickly create our input Allocation from a
Bitmap:
In this example the input image never changes so we never need to modify the input Allocation
again. We can reuse it each time the saturationLevel changes to create a new output Bitmap.
Creating the output Allocation is a little more complex. First we need to create what's called a Type
. A Type is used to tell an Allocation with what kind of data it's dealing with. Usually one uses the
Type.Builder class to quickly create an appropriate Type. Let's take a look at the code first:
https://riptutorial.com/ 1161
.create();
We are working with a normal 32 bit (or in other words 4 byte) per pixel Bitmap with 4 color
channels. That's why we are choosing Element.RGBA_8888 to create the Type. Then we use the
methods setX() and setY() to set the width and height of the output image to the same size as the
input image. The method create() then creates the Type with the parameters we specified.
Once we have the correct Type we can create the output Allocation with the static method
createTyped():
Now we are almost done. We also need an output Bitmap in which we can copy the data from the
output Allocation. To do this we use the static method createBitmap() to create a new empty Bitmap
with the same size and configuration as the input Bitmap.
And with that we have all the puzzle pieces to execute our RenderScript.
Full example
Now let's put all this together in one example:
https://riptutorial.com/ 1162
// Set the saturation level
script.set_saturationLevel(2.0f);
Conclusion
With this introduction you should be all set to write your own RenderScript Kernels for simple
image manipulation. However there are a few things you have to keep in mind:
If you want to quickly get started and play around with actual code I recommend you take a look at
the example GitHub project which implements the exact example talked about in this tutorial. You
can find the project here. Have fun with RenderScript!
Blur an image
This example demonstrates how to use Renderscript API to blur an image (using Bitmap). This
example uses ScriptInstrinsicBlur provided by android Renderscript API (API >= 17).
https://riptutorial.com/ 1163
private int width;
private int height;
if (outAllocation != null) {
outAllocation.destroy();
outAllocation = null;
}
if (blurScript != null) {
blurScript.destroy();
blurScript = null;
}
if (inAllocation != null) {
inAllocation.destroy();
inAllocation = null;
}
if (outAllocation != null) {
outAllocation.destroy();
outAllocation = null;
}
}
https://riptutorial.com/ 1164
// process and set data to the output allocation
blurScript.forEach(outAllocation);
if (createNewBitmap) {
Bitmap returnVal = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
outAllocation.copyTo(returnVal);
return returnVal;
}
outAllocation.copyTo(bitmap);
return bitmap;
}
}
Each script has a kernel which processes the data and it is generally invoked via forEach method.
@Override
public void onCreate(Bundle savedInstanceState) {
// setup layout and other stuff
// Blur image
Bitmap blurImage = blurProcessor.process(bitmap, true); // Use newBitamp as false if
you don't want to create a new bitmap
}
}
This concluded the example here. It is advised to do the processing in a background thread.
Blur a View
BlurBitmapTask.java
https://riptutorial.com/ 1165
// the ImageView can be garbage collected
imageViewReference = new WeakReference<>(imageView);
renderScript = RenderScript.create(context);
}
imageView.setImageBitmap(bitmap);
}
//Let's create an empty bitmap with the same size of the bitmap we want to blur
Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
//Create the in/out Allocations with the Renderscript and the in/out bitmaps
Allocation allIn = Allocation.createFromBitmap(renderScript, bitmap);
Allocation allOut = Allocation.createFromBitmap(renderScript, outBitmap);
//Copy the final bitmap created by the out Allocation to the outBitmap
allOut.copyTo(outBitmap);
https://riptutorial.com/ 1166
//After finishing everything, we destroy the Renderscript.
renderScript.destroy();
return outBitmap;
}
Usage:
ImageView imageViewOverlayOnViewToBeBlurred
.setImageDrawable(ContextCompat.getDrawable(this, android.R.color.transparent));
View viewToBeBlurred.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_LOW);
viewToBeBlurred.setDrawingCacheEnabled(true);
BlurBitmapTask blurBitmapTask = new BlurBitmapTask(this, imageViewOverlayOnViewToBeBlurred);
blurBitmapTask.execute(Bitmap.createBitmap(viewToBeBlurred.getDrawingCache()));
viewToBeBlurred.setDrawingCacheEnabled(false);
https://riptutorial.com/ 1167
Chapter 205: Resources
Examples
Translate a string
Strings can be internationalised by defining a different strings.xml for each language you support.
You add a new language by creating a new values directory with the ISO language code as a
suffix. For example, when adding a German set your structure might look like follows:
When the system looks for the requested string it first checks the language-specific xml, if it is not
found, the value from the default strings.xml file is returned. The key remains the same for each
language and only the value changes.
Example contents:
/res/values/strings.xml
/res/values-fr/strings.xml
https://riptutorial.com/ 1168
Define strings
Strings are typically stored in the resource file strings.xml. They are defined using a <string> XML
element.
The purpose of strings.xml is to allow internationalisation. You can define a strings.xml for each
language iso code. Thus when the system looks for the string 'app_name' it first checks the xml
file corresponding to the current language, and if it is not found, looks for the entry in the default
strings.xml file. This means you can choose to only localise some of your strings while not others.
/res/values/strings.xml
Once a string is defined in an XML resource file, it can be used by other parts of the app.
An app's XML project files can use a <string> element by referring to @string/string_name. For
example, an app's manifest (/manifests/AndroidManifest.xml) file includes the following line by
default in Android Studio:
android:label="@string/app_name"
This tells android to look for a <string> resource called "app_name" to use as the name for the app
when it is installed or displayed in a launcher.
Another time you would use a <string> resource from an XML file in android would be in a layout
file. For example, the following represents a TextView which displays the hello_world string we
defined earlier:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"/>
You can also access <string> resources from the java portion of your app. To recall our same
hello_world string from above within an Activity class, use:
res/values/filename.xml
https://riptutorial.com/ 1169
<string-array name="string_array_name">
<item>text_string</item>
<item>@string/string_id</item>
</string-array>
for example
res/values/arrays.xml
Output
Define dimensions
Dimensions are typically stored in a resource file names dimens.xml. They are defined using a
<dimen> element.
res/values/dimens.xml
<dimen name="small_font">14sp</dimen>
<dimen name="medium_font">16sp</dimen>
<dimen name="large_font">20sp</dimen>
</resources>
https://riptutorial.com/ 1170
• im : Inches
For example:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/large_padding">
</RelativeLayout>
Define integers
Integers are typically stored in a resource file named integers.xml, but the file name can be
chosen arbitrarily. Each integer is defined by using an <integer> element, as shown in the following
file:
res/values/integers.xml
Integers can now be referenced in XML with the syntax @integer/name_of_the_integer, as shown in
the following example:
<ProgressBar
android:layout_width="match_parent"
android:layout_height="match_parent"
android:max="@integer/max"/>
res/values/filename.xml
<integer-array name="integer_array_name">
<item>integer_value</item>
<item>@integer/integer_id</item>
</integer-array>
for example
res/values/arrays.xml
https://riptutorial.com/ 1171
<resources>
<integer-array name="fibo">
<item>@integer/zero</item>
<item>@integer/one</item>
<item>@integer/one</item>
<item>@integer/two</item>
<item>@integer/three</item>
<item>@integer/five</item>
</integer-array>
</resources>
Output
I/TAG: [0, 1, 1, 2, 3, 5]
Define colors
Colors are usually stored in a resource file named colors.xml in the /res/values/ folder.
<color name="blackOverlay">#66000000</color>
</resources>
Colors are represented by hexadecimal color values for each color channel (0 - FF) in one of the
formats:
• #RGB
• #ARGB
• #RRGGBB
• #AARRGGBB
Legend
https://riptutorial.com/ 1172
For example:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/blackOverlay">
These examples assume this is an Activity reference. A Context reference can be used in its
place as well.
1.6
6.0
In above declaration colorPrimary, colorPrimaryDark and colorAccent are used to define Material
design colors that will be used in defining custom Android theme in styles.xml. They are
automatically added when new project is created with Android Studio.
Using the Android API 23 or higher, very often such situation can be seen:
This situation is caused by the structural change of the Android API regarding getting the
resources.
Now the function:
public int getColor(@ColorRes int id, @Nullable Theme theme) throws NotFoundException
com.android.support:support-v4:24.0.0
ContextCompat.getColor(context, R.color.colorPrimaryDark);
https://riptutorial.com/ 1173
ContextCompat.getDrawable(context, R.drawable.btn_check);
ContextCompat.getColorStateList(context, R.color.colorPrimary);
DrawableCompat.setTint(drawable);
ContextCompat.getColor(context,R.color.colorPrimaryDark));
ViewCompat.setElevation(textView, 1F);
ViewCompat.animate(textView);
TextViewCompat.setTextAppearance(textView, R.style.AppThemeTextStyle);
...
<item
android:id="@+id/first_item_id"
android:orderInCategory="100"
android:title="@string/first_item_string"
android:icon="@drawable/first_item_icon"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/second_item_id"
android:orderInCategory="110"
android:title="@string/second_item_string"
android:icon="@drawable/second_item_icon"
app:showAsAction="ifRoom"/>
</menu>
Inside Activity:
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
///Override defining menu resource
inflater.inflate(R.menu.menu_resource_id, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
//Override for preparing items (setting visibility, change text, change icon...)
super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
https://riptutorial.com/ 1174
//Override it for handling items
int menuItemId = item.getItemId();
switch (menuItemId) {
case: R.id.first_item_id
return true; //return true, if is handled
}
return super.onOptionsItemSelected(item);
}
For invoking the methods above during showing the view, call
getActivity().invalidateOptionsMenu();
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState) {
setHasOptionsMenu(true);
super.onCreateView(inflater, container, savedInstanceState);
}
Defining Strings in the strings.xml file also allows for string formatting. The only caveat is that the
String will need to be dealt with in code like below, versus simply attaching it to a layout.
In above example,
%1$s
'%' separates from normal characters,
'1' denotes first parameter,
'$' is used as separator between parameter number and type,
's' denotes string type ('d' is used for integer)
Note that getString() is a method of Context or Resources, i.e. you can use it directly within an
Activity instance, or else you may use getActivity().getString() or getContext().getString()
respectively.
Color state lists can be used as colors, but will change depending on the state of the view they are
used for.
https://riptutorial.com/ 1175
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#888888" android:state_enabled="false"/>
<item android:color="@color/lightGray" android:state_selected="false"/>
<item android:color="@android:color/white" />
</selector>
Items are evaluated in the order they are defined, and the first item whose specified states match
the current state of the view is used. So it's a good practice to specify a catch-all at the end,
without any state selectors specified.
Each item can either use a color literal, or reference a color defined somewhere else.
To differentiate between plural and singular strings, you can define a plural in your strings.xml file
and list the different quantities, as shown in the example below:
This definition can be accessed from Java code by using the getQuantityString() method of the
Resources class, as shown in the following example:
getResources().getQuantityString(R.plurals.hello_people, 3, 3);
Here, the first parameter R.plurals.hello_people is the resource name. The second parameter (3 in
this example) is used to pick the correct quantity string. The third parameter (also 3 in this
example) is the format argument that will be used for substituting the format specifier %d.
few
many
one
other
two
zero
It is important to note that not all locales support every denomination of quantity. For example, the
Chinese language does not have a concept of one item. English does not have a zero item, as it is
grammatically the same as other. Unsupported instances of quantity will be flagged by the IDE as
Lint warnings, but won't cause complication errors if they are used.
https://riptutorial.com/ 1176
There are cases, where custom objects need to be created and defined in the resources of the
application. Such objects can be composed of Java simple types, for example Integer, Float, String
.
Here is the example of how to import an object defined in application resources. The object
Category constains 3 properties of category:
• ID
• Color
• Name
This POJO has it's equivalent in categories.xml file, where each of array has the same properties
defined for each category.
categories.xml
https://riptutorial.com/ 1177
3. Compose each model consisting of resources:
<array name="no_action">
<item>0</item>
<item>@android:color/transparent</item>
<item>@string/statusRegistration</item>
</array>
<array name="to_accept">
<item>1</item>
<item>@color/light_gray</item>
<item>@string/acceptance</item>
</array>
<array name="opened">
<item>2</item>
<item>@color/material_green_500</item>
<item>@string/open</item>
</array>
<array name="to_verify">
<item>3</item>
<item>@color/material_gray_800</item>
<item>@string/verification</item>
</array>
<array name="to_close">
<item>4</item>
<item>@android:color/black</item>
<item>@string/closed</item>
</array>
<array name="categories">
<item>@array/no_action</item>
<item>@array/to_accept</item>
<item>@array/opened</item>
<item>@array/to_verify</item>
<item>@array/to_close</item>
</array>
@NonNull
public List<Category> getCategories(@NonNull Context context) {
final int DEFAULT_VALUE = 0;
final int ID_INDEX = 0;
final int COLOR_INDEX = 1;
final int LABEL_INDEX = 2;
if (context == null) {
return Collections.emptyList();
}
// Get the array of objects from the `tasks_categories` array
TypedArray statuses = context.getResources().obtainTypedArray(R.array.categories);
if (statuses == null) {
return Collections.emptyList();
}
List<Category> categoryList = new ArrayList<>();
for (int i = 0; i < statuses.length(); i++) {
int statusId = statuses.getResourceId(i, DEFAULT_VALUE);
https://riptutorial.com/ 1178
// Get the properties of one object
TypedArray rawStatus = context.getResources().obtainTypedArray(statusId);
category.setColor(rawStatus.getResourceId(COLOR_INDEX, DEFAULT_VALUE));
categoryList.add(taskCategory);
}
return taskCategoryList;
}
9 Patches
9 Patches are stretchable images in which the areas which can be stretched are defined by black
markers on a transparent border.
Unfortunately, recently that page has been put down for a while (it's currently up again).
Hence, the need to have a physical copy of that page for android developers on our reliable
server/s.
Here it is.
https://riptutorial.com/ 1179
A SIMPLE GUIDE TO 9-PATCH FOR ANDROID UI May 18,
2011
While I was working on my first Android app, I found 9-patch (aka 9.png) to be confusing and
poorly documented. After a little while, I finally picked up on how it works and decided to throw
together something to help others figure it out.
Basically, 9-patch uses png transparency to do an advanced form of 9-slice or scale9. The guides
are straight, 1-pixel black lines drawn on the edge of your image that define the scaling and fill of
your image. By naming your image file name.9.png, Android will recognize the 9.png format and
use the black guides to scale and fill your bitmaps.
As you can see, you have guides on each side of your image. The TOP and LEFT guides are for
scaling your image (i.e. 9-slice), while the RIGHT and BOTTOM guides define the fill area.
The black guide lines are cut-off/removed from your image – they won’t show in the app. Guides
must only be one pixel wide, so if you want a 48×48 button, your png will actually be 50×50.
Anything thicker than one pixel will remain part of your image. (My examples have 4-pixel wide
guides for better visibility. They should really be only 1-pixel).
Your guides must be solid black (#000000). Even a slight difference in color (#000001) or alpha
will cause it to fail and stretch normally. This failure won’t be obvious either*, it fails silently! Yes.
Really. Now you know.
Also you should keep in mind that remaining area of the one-pixel outline must be completely
transparent. This includes the four corners of the image – those should always be clear. This can
be a bigger problem than you realize. For example, if you scale an image in Photoshop it will add
https://riptutorial.com/ 1180
anti-aliased pixels which may include almost-invisible pixels which will also cause it to fail*. If you
must scale in Photoshop, use the Nearest Neighbor setting in the Resample Image pulldown
menu (at the bottom of the Image Size pop-up menu) to keep sharp edges on your guides.
*(updated 1/2012) This is actually a “fix” in the latest dev kit. Previously it would manifest itself as
all of your other images and resources suddenly breaking, not the actually broken 9-patch image.
The TOP and LEFT guides are used to define the scalable portion of your image – LEFT for
scaling height, TOP for scaling width. Using a button image as an example, this means the button
can stretch horizontally and vertically within the black portion and everything else, such as the
corners, will remain the same size. The allows you to have buttons that can scale to any size and
maintain a uniform look.
It’s important to note that 9-patch images don’t scale down – they only scale up. So it’s best to
start as small as possible.
Also, you can leave out portions in the middle of the scale line. So for example, if you have a
button with a sharp glossy edge across the middle, you can leave out a few pixels in the middle of
the LEFT guide. The center horizontal axis of your image won’t scale, just the parts above and
below it, so your sharp gloss won’t get anti-aliased or fuzzy.
https://riptutorial.com/ 1181
Fill area guides are optional and provide a way define the area for stuff like your text label. Fill
determines how much room there is within your image to place text, or an icon, or other things. 9-
patch isn’t just for buttons, it works for background images as well.
The above button & label example is exaggerated simply to explain the idea of fill – the label isn’t
completely accurate. To be honest, I haven’t experienced how Android does multi-line labels since
a button label is usually a single row of text.
Finally, here’s a good demonstration of how scale and fill guides can vary, such as a LinearLayout
with a background image & fully rounded sides:
With this example, the LEFT guide isn’t used but we’re still required to have a guide. The
background image don’t scale vertically; it just scales horizontally (based on the TOP guide).
Looking at the fill guides, the RIGHT and BOTTOM guides extend beyond where they meet the
image’s curved edges. This allows me to place my round buttons close to the edges of the
background for a tight, fitted look.
So that’s it. 9-patch is super easy, once you get it. It’s not a perfect way to do scaling, but the fill-
https://riptutorial.com/ 1182
area and multi-line scale-guides does offer more flexibility than traditional 9-slice and scale9. Give
it a try and you’ll figure it out quickly.
------------------------------
| Alpha(%) | Hex Value |
------------------------------
| 100% | FF |
| 95% | F2 |
| 90% | E6 |
| 85% | D9 |
| 80% | CC |
| 75% | BF |
| 70% | B3 |
| 65% | A6 |
| 60% | 99 |
| 55% | 8C |
| 50% | 80 |
| 45% | 73 |
| 40% | 66 |
| 35% | 59 |
| 30% | 4D |
| 25% | 40 |
| 20% | 33 |
| 15% | 26 |
| 10% | 1A |
| 5% | 0D |
| 0% | 00 |
------------------------------
<color name="red_with_alpha_45">#73FF0000</color>
A string resource provides text strings for your application with optional text styling and formatting.
There are three types of resources that can provide your application with strings:
String
Syntax:
https://riptutorial.com/ 1183
<resources>
<string name="string_name">text_string</string>
</resources>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/string_name" />
String Array
Syntax:
<resources>
<string-array name="planets_array">
<item>Mercury</item>
<item>Venus</item>
<item>Earth</item>
<item>Mars</item>
</string-array>
Usage
Syntax:
Usage:
https://riptutorial.com/ 1184
Read Resources online: https://riptutorial.com/android/topic/108/resources
https://riptutorial.com/ 1185
Chapter 206: Retrofit2
Introduction
The official Retrofit page describes itself as
Retrofit turns your REST API into a Java interface. It uses annotations to describe HTTP requests,
URL parameter replacement and query parameter support is integrated by default. Additionally, it
provides functionality for multipart request body and file uploads.
Remarks
Dependencies for the retrofit library:
Gradle:
dependencies {
...
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
...
}
Maven:
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.3.0</version>
</dependency>
Examples
A Simple GET Request
We are going to be showing how to make a GET request to an API that responds with a JSON object
or a JSON array. The first thing we need to do is add the Retrofit and GSON Converter dependencies
to our module's gradle file.
Add the dependencies for retrofit library as described in the Remarks section.
https://riptutorial.com/ 1186
{
"deviceId": "56V56C14SF5B4SF",
"name": "Steven",
"eventCount": 0
}
[
{
"deviceId": "56V56C14SF5B4SF",
"name": "Steven",
"eventCount": 0
},
{
"deviceId": "35A80SF3QDV7M9F",
"name": "John",
"eventCount": 2
}
]
@SerializedName("name")
public String name;
@SerializedName("eventCount")
public int eventCount;
}
The @SerializedName annotations here are from the GSON library and allows us to serialize and
deserialize this class to JSON using the serialized name as the keys. Now we can build the
interface for the API that will actually fetch the data from the server.
@GET("devices")
Call<List<Device>> getDevices();
}
There's a lot going on here in a pretty compact space so let's break it down:
• The @GET annotation comes from Retrofit and tells the library that we're defining a GET
request.
• The path in the parentheses is the endpoint that our GET request should hit (we'll set the
base url a little later).
https://riptutorial.com/ 1187
• The curly-brackets allow us to replace parts of the path at run time so we can pass
arguments.
• The function we're defining is called getDevice and takes the device id we want as an
argument.
• The @PATH annotation tells Retrofit that this argument should replace the "deviceId"
placeholder in the path.
• The function returns a Call object of type Device.
Now we will make a little wrapper class for our API to keep the Retrofit initialization code wrapped
up nicely.
private DeviceAPIHelper ()
{
api = retrofit.create(DeviceAPI.class);
}
}
This class creates a GSON instance to be able to parse the JSON response, creates a Retrofit
instance with our base url and a GSONConverter and then creates an instance of our API.
@Override
public void onFailure (Call<Device> call, Throwable t)
{
Log.e(TAG, t.getLocalizedMessage());
}
});
https://riptutorial.com/ 1188
callArray.enqueue(new Callback<Response<List<Device>>()
{
@Override
public void onResponse (Call<List<Device>> call, Response<List<Device>> response)
{
if (response.isSuccessful())
{
List<Device> devices = response.body();
}
}
@Override
public void onFailure (Call<List<Device>> call, Throwable t)
{
Log.e(TAG, t.getLocalizedMessage());
}
});
This uses our API interface to create a Call<Device> object and to create a Call<List<Device>>
respectively. Calling enqueue tells Retrofit to make that call on a background thread and return the
result to the callback that we're creating here.
Note: Parsing a JSON array of primitive objects (like String, Integer, Boolean, and Double) is
similar to parsing a JSON array. However, you don't need your own model class. You can get the
array of Strings for example by having the return type of the call as Call<List<String>>.
Retrofit requests can be logged using an intercepter. There are several levels of detail available:
NONE, BASIC, HEADERS, BODY. See Github project here.
compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
Exposing the logs in the Terminal(Android Monitor) is something that should be avoided in the
release version as it may lead to unwanted exposing of critical information such as Auth Tokens
etc.
To avoid the logs being exposed in the run time, check the following condition
https://riptutorial.com/ 1189
if(BuildConfig.DEBUG){
//your interfector code here
}
For example:
This example shows how to use a request interceptor with OkHttp. This has numerous use cases
such as:
https://riptutorial.com/ 1190
• Adding universal header to the request. E.g. authenticating a request
• Debugging networked applications
• Retrieving raw response
• Logging network transaction etc.
• Set custom user agent
if (!TextUtils.isEmpty(githubToken)) {
// `githubToken`: Access token for GitHub
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request newReq = request.newBuilder()
.addHeader("Authorization", format("token %s", githubToken))
.build();
return chain.proceed(newReq);
}
}).build();
builder.client(client);
}
return builder.build().create(GithubApi.class);
The @Header and @Body annotations can be placed into the method signatures and Retrofit will
automatically create them based on your models.
AuthenticaionRequest is our model, a POJO, containing the information the server requires. For
this example, our server wants the client key and secret.
https://riptutorial.com/ 1191
We create our Retrofit service somewhere. We make sure to use HTTPS.
Once you have setup the Retrofit environment in your project, you can use the following example
that demonstrates how to upload multiple files using Retrofit:
https://riptutorial.com/ 1192
@Override
public void onResponse(Call<String> call,
Response<String> response) {
Log.i(LOG_TAG, "success");
Log.d("body==>", response.body().toString() + "");
Log.d("isSuccessful==>", response.isSuccessful() + "");
Log.d("message==>", response.message() + "");
Log.d("raw==>", response.raw().toString() + "");
Log.d("raw().networkResponse()", response.raw().networkResponse().toString() +
"");
}
@Override
public void onFailure(Call<String> call, Throwable t) {
Log.e(LOG_TAG, t.getMessage());
}
});
}
public String getRealPathFromUri(final Uri uri) { // function for file path from uri,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT &&
DocumentsContract.isDocumentUri(mContext, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
https://riptutorial.com/ 1193
return getDataColumn(mContext, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return null;
}
The option 1 is used for downloading a file from Server which is having fixed URL. and option 2 is
used to pass a dynamic value as full URL to request call. This can be helpful when downloading
files, which are dependent of parameters like user or time.
https://riptutorial.com/ 1194
public static final String API_BASE_URL = "http://your.api-base.url/";
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()){
boolean writtenToDisk = writeResponseBodyToDisk(response.body());
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
}
And after getting response in the callback, code some standard IO for saving file to disk. Here is
the code:
try {
byte[] fileReader = new byte[4096];
https://riptutorial.com/ 1195
inputStream = body.byteStream();
outputStream = new FileOutputStream(futureStudioIconFile);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
outputStream.flush();
return true;
} catch (IOException e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return false;
}
}
Note we have specified ResponseBody as return type, otherwise Retrofit will try to parse and
convert it, which doesn't make sense when you are downloading file.
If you want more on Retrofit stuffs, got to this link as it is very useful. [1]:
https://futurestud.io/blog/retrofit-getting-started-and-android-client
compile 'com.facebook.stetho:stetho:1.5.0'
compile 'com.facebook.stetho:stetho-okhttp3:1.5.0'
Stetho.initializeWithDefaults(this);
https://riptutorial.com/ 1196
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.addNetworkInterceptor(new StethoInterceptor());
Now connect your phone to your computer, launch the app, and type chrome://inspect into your
Chrome browser. Retrofit network calls should now show up for you to inspect.
dependencies {
....
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile ('com.thoughtworks.xstream:xstream:1.4.7') {
exclude group: 'xmlpull', module: 'xmlpull'
}
....
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[]
annotations, Retrofit retrofit) {
https://riptutorial.com/ 1197
Class<?> cls = (Class<?>) type;
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit
retrofit) {
@Override
public T convert(ResponseBody value) throws IOException {
try {
this.xStream.processAnnotations(cls);
Object object = this.xStream.fromXML(value.byteStream());
return (T) object;
}finally {
value.close();
}
}
}
XStreamXmlRequestBodyConverter(XStream xStream) {
this.xStream = xStream;
}
https://riptutorial.com/ 1198
@Override
public RequestBody convert(T value) throws IOException {
try {
OutputStreamWriter osw = new OutputStreamWriter(buffer.outputStream(), CHARSET);
xStream.toXML(value, osw);
osw.flush();
} catch (Exception e) {
throw new RuntimeException(e);
}
So, this point we can send and receive any XML , We just need create XStream Annotations for
the entities.
Sample JSON:
{
"id": "12345",
"type": "android"
}
@SerializedName("deviceId")
private String mDeviceId;
https://riptutorial.com/ 1199
Define your service (endpoints to hit):
@POST("device")
Call<Device> getDevice(@Body GetDeviceRequest getDeviceRequest);
static {
setupRestClient();
}
// Define gson
Gson gson = new Gson();
REST_CLIENT = retrofit.create(Service.class);
}
@SerializedName("id")
private String mId;
@SerializedName("type")
private String mType;
https://riptutorial.com/ 1200
Define controller to handle the requests for the device
@Override
public void onFailure(Throwable t) {
// Go ahead and handle the error here
}
});
We will use retrofit 2 and SimpleXmlConverter to get xml data from url and parse to Java class.
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-simplexml:2.1.0'
Create interface
@GET (data/read.xml)
Call<Rss> getData();
https://riptutorial.com/ 1201
private void readXmlFeed() {
try {
@Override
public void onResponse(Call<Rss> call, Response<Rss> response) {
@Override
public void onFailure(Call<Rss> call, Throwable t) {
Log.e("Response fail", t.getMessage());
}
});
} catch (Exception e) {
Log.e("Exception", e.getMessage());
}
public Rss() {
public Rss(String title, String description, String link, List<Item> item, String
language) {
this.title = title;
this.description = description;
this.link = link;
this.item = item;
this.language = language;
https://riptutorial.com/ 1202
}
@Element(name = "description")
private String description;
@Element(name = "link")
private String link;
@Element(name = "language")
private String language;
https://riptutorial.com/ 1203
Chapter 207: Retrofit2 with RxJava
Examples
Retrofit2 with RxJava
dependencies {
....
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
....
}
Create an interface containing methods used to exchange data with remote server:
@GET("api/get-servers")
public Observable<List<Server>> getServers();
}
api = retrofit.create(ApiServerRequests.class);
return api;
}
https://riptutorial.com/ 1204
apiRequests.getServers()
.subscribeOn(Schedulers.io()) // the observable is emitted on io thread
.observerOn(AndroidSchedulers.mainThread()) // Methods needed to handle request in
background thread
.subscribe(new Subscriber<List<Server>>() {
@Override
public void onCompleted() {
@Override
public void onError(Throwable e) {
@Override
public void onNext(List<Server> servers) {
//A list of servers is fetched successfully
}
});
From the GitHub repo of RxJava, RxJava is a Java VM implementation of Reactive Extensions: a
library for composing asynchronous and event-based programs by using observable sequences. It
extends the observer pattern to support sequences of data/events and adds operators that allow
you to compose sequences together declaratively while abstracting away concerns about things
like low-level threading, synchronisation, thread-safety and concurrent data structures.
Retrofit is a type-safe HTTP client for Android and Java, using this, developers can make all
network stuff much more easier. As an example, we are going to download some JSON and show
it in RecyclerView as a list.
Getting started:
Add RxJava, RxAndroid and Retrofit dependencies in your app level build.gradle file: compile
"io.reactivex:rxjava:1.1.6"
compile "io.reactivex:rxandroid:1.2.1"
compile "com.squareup.retrofit2:adapter-rxjava:2.0.2"
compile "com.google.code.gson:gson:2.6.2"
compile "com.squareup.retrofit2:retrofit:2.0.2"
compile "com.squareup.retrofit2:converter-gson:2.0.2"
https://riptutorial.com/ 1205
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofitInstance;
}
return restAdapter.create(clazz);
}
@GET("repos/{org}/{repo}/issues")
Observable<List<Issue>> getIssues(@Path("org") String organisation,
@Path("repo") String repositoryName,
@Query("page") int pageNumber);}
Note the getRepos() is returning an Observable and not just a list of issues.
An example for this is shown. You can use free services like JsonSchema2Pojo or this.
@SerializedName("url")
@Expose
private String url;
@SerializedName("html_url")
@Expose
private String htmlUrl;
https://riptutorial.com/ 1206
@Override
public void onCompleted() {
Log.i(TAG, "onCompleted: COMPLETED!");
}
@Override
public void onError(Throwable e) {
Log.e(TAG, "onError: ", e);
}
@Override
public void onNext(List<Issue> issues) {
recyclerView.setAdapter(new IssueAdapter(MainActivity.this, issues,
apiService));
}
});
Now, you have successfully fetched data from a server using Retrofit and RxJava.
Suppose we have an API which allows us to get object metadata in single request (getAllPets),
and other request which have full data of single resource (getSinglePet). How we can query all of
them in a single chain?
interface PetApi {
PetApi petApi;
Disposable petsDisposable;
petApi.getAllPets()
.doOnSubscribe(new Consumer<Disposable>() {
@Override public void accept(Disposable disposable) throws Exception {
petsDisposable = disposable;
}
https://riptutorial.com/ 1207
})
.flatMap(new Function<PetRepository, ObservableSource<Integer>>() {
@Override
public ObservableSource<Integer> apply(PetRepository petRepository) throws
Exception {
List<Integer> petIds = petRepository.ids;
return Observable.fromIterable(petIds);
}
})
.flatMap(new Function<Integer, ObservableSource<Pet>>() {
@Override public ObservableSource<Pet> apply(Integer id) throws Exception {
return petApi.getSinglePet(id);
}
})
.toList()
.toObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<Pet>>() {
@Override public void accept(List<Pet> pets) throws Exception {
//use your pets here
}
}, new Consumer<Throwable>() {
@Override public void accept(Throwable throwable) throws Exception {
//show user something goes wrong
}
});
void cancelRequests(){
if (petsDisposable!=null){
petsDisposable.dispose();
petsDisposable = null;
}
}
https://riptutorial.com/ 1208
Chapter 208: RoboGuice
Examples
Simple example
RoboGuice is a framework that brings the simplicity and ease of Dependency Injection to Android,
using Google's own Guice library.
@ContentView(R.layout.main)
class RoboWay extends RoboActivity {
@InjectView(R.id.name) TextView name;
@InjectView(R.id.thumbnail) ImageView thumbnail;
@InjectResource(R.drawable.icon) Drawable icon;
@InjectResource(R.string.app_name) String myName;
@Inject LocationManager loc;
Add the following pom to the dependencies section of your gradle build file :
project.dependencies {
compile 'org.roboguice:roboguice:3.+'
provided 'org.roboguice:roboblender:3.+'
}
@ContentView annotation
The @ContentView annotation can be used to further alleviate development of activities and
replace the setContentView statement :
@ContentView(R.layout.myactivity_layout)
public class MyActivity extends RoboActivity {
@InjectView(R.id.text1) TextView textView;
@Override
protected void onCreate( Bundle savedState ) {
textView.setText("Hello!");
}
}
@InjectResource annotation
You can inject any type of resource, Strings, Animations, Drawables, etc.
https://riptutorial.com/ 1209
To inject your first resource into an activity, you'll need to:
Example
@InjectView annotation
Example
Introduction to RoboGuice
RoboGuiceis a framework that brings the simplicity and ease of Dependency Injection to Android,
using Google's own Guice library.
RoboGuice 3 slims down your application code. Less code means fewer opportunities for bugs. It
also makes your code easier to follow -- no longer is your code littered with the mechanics of the
Android platform, but now it can focus on the actual logic unique to your application.
To give you an idea, take a look at this simple example of a typical Android Activity:
https://riptutorial.com/ 1210
name = (TextView) findViewById(R.id.name);
thumbnail = (ImageView) findViewById(R.id.thumbnail);
loc = (LocationManager) getSystemService(Activity.LOCATION_SERVICE);
icon = getResources().getDrawable(R.drawable.icon);
myName = getString(R.string.app_name);
name.setText( "Hello, " + myName );
}
}
This example is 19 lines of code. If you're trying to read through onCreate(), you have to skip over
5 lines of boilerplate initialization to find the only one that really matters: name.setText(). And
complex activities can end up with a lot more of this sort of initialization code.
@ContentView(R.layout.main)
class RoboWay extends RoboActivity {
@InjectView(R.id.name) TextView name;
@InjectView(R.id.thumbnail) ImageView thumbnail;
@InjectResource(R.drawable.icon) Drawable icon;
@InjectResource(R.string.app_name) String myName;
@Inject LocationManager loc;
RoboGuice's goal is to make your code be about your app, rather than be about all the
initialization and lifecycle code you typically have to maintain in Android.
Annotations:
@ContentView annotation:
The @ContentView annotation can be used to further alleviate development of activities and
replace the setContentView statement :
@ContentView(R.layout.myactivity_layout)
public class MyActivity extends RoboActivity {
@InjectView(R.id.text1) TextView textView;
@Override
protected void onCreate( Bundle savedState ) {
textView.setText("Hello!");
}
}
@InjectResource annotation:
First you need an Activity that inherits from RoboActivity. Then, assuming that you have an
animation my_animation.xml in your res/anim folder, you can now reference it with an annotation:
https://riptutorial.com/ 1211
public class MyActivity extends RoboActivity {
@InjectResource(R.anim.my_animation) Animation myAnimation;
// the rest of your code
}
@Inject annotation:
You make sure your activity extends from RoboActivity and annotate your System service member
with @Inject. Roboguice will do the rest.
In addition to Views, Resources, Services, and other android-specific things, RoboGuice can inject
Plain Old Java Objects. By default Roboguice will call a no argument constructor on your POJO
https://riptutorial.com/ 1212
Chapter 209: Robolectric
Introduction
Unit testing is taking a piece of code and testing it independently without any other dependencies
or parts of the system running (for example the database).
Robolectric is a unit test framework that de-fangs the Android SDK jar so you can test-drive the
development of your Android app. Tests run inside the JVM on your workstation in seconds.
Combing them both allows you to run fast tests on the JVN still using the Android API's.
Examples
Robolectric test
@RunWith(RobolectricTestRunner.class)
public class MyActivityTest {
@Test
public void clickingButton_shouldChangeResultsViewText() throws Exception {
MyActivity activity = Robolectric.setupActivity(MyActivity.class);
button.performClick();
assertThat(results.getText().toString()).isEqualTo("Robolectric Rocks!");
}
}
Configuration
@RunWith(RobolectricTestRunner.class)
@Config(application = MyApplication.class)
public final class MyTest {
}
@RunWith(RobolectricTestRunner.class)
@Config(sdk = Build.VERSION_CODES.LOLLIPOP)
public final class MyTest {
https://riptutorial.com/ 1213
}
@RunWith(RobolectricTestRunner.class)
@Config(manifest = "path/AndroidManifest.xml")
public final class MyTest {
}
Use qualifiers
Possible qualifiers can be found in android docs.
@RunWith(RobolectricTestRunner.class)
public final class MyTest {
@Config(qualifiers = "sw600dp")
public void testForTablet() {
}
}
https://riptutorial.com/ 1214
Chapter 210: Runtime Permissions in API-23
+
Introduction
Android Marshmallow introduced Runtime Permission model. Permissions are categorized into
two categories i.e. Normal and Dangerous Permissions. where dangerous permissions are now
granted by the user at run time.
Remarks
From sdk 23 Android requires runtime permissions for permissions on devices running Android 6.0
and higher, within what is classed as the Dangerous Permission Groups. Dangerous permission
groups are one's that are considered to compromise the user's privacy and/or security.
Permission Group
CALENDAR
CAMERA
CONTACTS
LOCATION
MICROPHONE
PHONE
SENSORS
SMS
STORAGE
Any permissions from these groups requires management of runtime permissions for devices on
Android 6.0 and higher with a target sdk of 23 or higher.
Normal Permissions
The following is a list of normal permissions. These are not regarded as dangerous to the user's
privacy or security and so do not require runtime permissions for sdk 23 and higher.
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
https://riptutorial.com/ 1215
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
Examples
Android 6.0 multiple permissions
This example shows how to check permissions at runtime in Android 6 and later.
@Override
void onStart() {
if (checkPermissions()){
// permissions granted.
} else {
// show dialog informing them that we lack certain permissions
}
https://riptutorial.com/ 1216
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[]
grantResults) {
switch (requestCode) {
case MULTIPLE_PERMISSIONS:{
if(grantResults.length > 0 && grantResults[0] ==
PackageManager.PERMISSION_GRANTED){
// permissions granted.
} else {
// no permissions granted.
}
return;
}
}
}
You can do a permissions check when sending an Intent to a registered broadcast receiver. The
permissions you send are cross-checked with the ones registered under the tag. They restrict who
can send broadcasts to the associated receiver.
To send a broadcast request with permissions, specify the permission as a string in the
Context.sendBroadcast(Intent intent, String permission) call, but keep in mind that the receiver's
app MUST have that permission in order to receive your broadcast. The reciever should be
installed first before the sender.
and you can specify in your manifest that the broadcast sender is required to include the
requested permission sent through the sendBroadcast:
https://riptutorial.com/ 1217
<!-- Your special permission -->
<permission android:name="org.quadcore.mypermission"
android:label="my_permission"
android:protectionLevel="dangerous"></permission>
Also declare the permission in the manifest of the application that is supposed to receive this
broadcast:
Note: Both a receiver and a broadcaster can require a permission, and when this happens, both
permission checks must pass for the Intent to be delivered to the associated target. The App that
defines the permission should be installed first.
In the manifest we have fours dangerous runtime permissions from two groups.
In the activity where the permissions are required. Note it is important to check for permissions in
any activity that requires permissions, as the permissions can be revoked while the app is in the
background and the app will then crash.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_layout);
We only need to ask for permission for one of these from each group and all other permissions
from this group are granted unless the permission is revoked by the user.
https://riptutorial.com/ 1218
if (Build.VERSION.SDK_INT >= 23) {
List<String> permissionsNeeded = new ArrayList<String>();
List<String> permissionsList = new ArrayList<String>();
if (!addPermission(permissionsList, android.Manifest.permission.ACCESS_FINE_LOCATION))
{
permissionsNeeded.add("GPS");
}
if (!addPermission(permissionsList,
android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
permissionsNeeded.add("Read Storage");
}
if (permissionsList.size() > 0) {
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
return;
}
}
}
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permission);
This deals with the result of the user allowing or not allowing permissions. In this example, if the
permissions are not allowed, the app is killed.
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[]
grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS: {
https://riptutorial.com/ 1219
PackageManager.PERMISSION_GRANTED) {
// All Permissions Granted
return;
} else {
// Permission Denied
if (Build.VERSION.SDK_INT >= 23) {
Toast.makeText(
getApplicationContext(),
"My App cannot run without Location and Storage " +
"Permissions.\nRelaunch My App or allow permissions" +
" in Applications Settings",
Toast.LENGTH_LONG).show();
finish();
}
}
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
Using PermissionUtil
PermissionUtil is a simple and convenient way of asking for permissions in context. You can easily
provide what should happen in case of all requested permissions granted (onAllGranted()), any
request was denied (onAnyDenied()) or in case that a rational is needed (onRational()).
Anywhere in your AppCompatActivity or Fragment that you want to ask for user's permisssion
mRequestObject =
PermissionUtil.with(this).request(Manifest.permission.WRITE_EXTERNAL_STORAGE).onAllGranted(
new Func() {
@Override protected void call() {
//Happy Path
}
}).onAnyDenied(
new Func() {
@Override protected void call() {
//Sad Path
}
}).ask(REQUEST_CODE_STORAGE);
if(mRequestObject!=null){
mRequestObject.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
https://riptutorial.com/ 1220
Include all permission-related code to an abstract base class and extend the
activity of this base class to achieve cleaner/reusable code
@Override
protected void onStart() {
super.onStart();
...
}
@Override
public void setContentView(int layoutResId) {
super.setContentView(layoutResId);
bindViews();
}
...
@Override
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionCallback callback = permissionCallbackMap.get(requestCode);
if (grantedPermissions.size() > 0) {
callback.onPermissionGranted(
grantedPermissions.toArray(new String[grantedPermissions.size()]));
}
if (deniedPermissions.size() > 0) {
callback.onPermissionDenied(
deniedPermissions.toArray(new String[deniedPermissions.size()]));
}
https://riptutorial.com/ 1221
if (blockedPermissions.size() > 0) {
callback.onPermissionBlocked(
blockedPermissions.toArray(new String[blockedPermissions.size()]));
}
permissionCallbackMap.remove(requestCode);
}
/**
* Check whether a permission is granted or not.
*
* @param permission
* @return
*/
public boolean hasPermission(String permission) {
return ContextCompat.checkSelfPermission(this, permission) ==
PackageManager.PERMISSION_GRANTED;
}
/**
* Request permissions and get the result on callback.
*
* @param permissions
* @param callback
*/
public void requestPermission(String [] permissions, @NonNull PermissionCallback callback)
{
int requestCode = permissionCallbackMap.size() + 1;
permissionCallbackMap.put(requestCode, callback);
ActivityCompat.requestPermissions(this, permissions, requestCode);
}
/**
* Request permission and get the result on callback.
*
* @param permission
* @param callback
*/
public void requestPermission(String permission, @NonNull PermissionCallback callback) {
int requestCode = permissionCallbackMap.size() + 1;
permissionCallbackMap.put(requestCode, callback);
ActivityCompat.requestPermissions(this, new String[] { permission }, requestCode);
}
}
https://riptutorial.com/ 1222
public void onPermissionGranted(String[] grantedPermissions) {
requestLocation();
}
@Override
public void onPermissionDenied(String[] deniedPermissions) {
// Do something.
}
@Override
public void onPermissionBlocked(String[] blockedPermissions) {
// Do something.
}
});
}
https://riptutorial.com/ 1223
Chapter 211: SearchView
Examples
Appcompat SearchView with RxBindings watcher
build.gradle:
dependencies {
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.4.0'
}
menu/menu.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
MainActivity.java:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
// optional: set the letters count after which the search will begin to 1
// the default is 2
try {
int autoCompleteTextViewID =
getResources().getIdentifier("android:id/search_src_text", null, null);
AutoCompleteTextView searchAutoCompleteTextView = (AutoCompleteTextView)
searchView.findViewById(autoCompleteTextViewID);
searchAutoCompleteTextView.setThreshold(1);
} catch (Exception e) {
Logs.e(TAG, "failed to set search view letters threshold");
https://riptutorial.com/ 1224
}
searchView.setOnSearchClickListener(v -> {
// optional actions to search view expand
});
searchView.setOnCloseListener(() -> {
// optional actions to search view close
return false;
});
RxSearchView.queryTextChanges(searchView)
.doOnEach(notification -> {
CharSequence query = (CharSequence) notification.getValue();
searchAdapter.filter(query);
})
.debounce(300, TimeUnit.MILLISECONDS) // to skip intermediate letters
.flatMap(query -> MyWebService.search(query)) // make a search request
.retry(3)
.subscribe(results -> {
searchAdapter.populateAdapter(results);
});
SearchAdapter.java
https://riptutorial.com/ 1225
@Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
int position = cursor.getPosition();
if (position < items.size()) {
SearchResult result = items.get(position);
// bind your view here
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v.setTag(holder);
return v;
}
public ViewHolder(View v) {
this.text= (TextView) v.findViewById(R.id.text);
}
}
}
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".HomeActivity">
<item
android:id="@+id/action_search"
android:icon="@android:drawable/ic_menu_search"
android:title="Search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always" />
</menu>
MainFragment.java
@Nullable
https://riptutorial.com/ 1226
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
SearchManager searchManager = (SearchManager)
getActivity().getSystemService(Context.SEARCH_SERVICE);
if (searchItem != null) {
searchView = (SearchView) searchItem.getActionView();
}
if (searchView != null) {
searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName()));
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
Log.i("onQueryTextSubmit", query);
return true;
}
};
searchView.setOnQueryTextListener(queryTextListener);
}
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_search:
// Not implemented here
return false;
default:
break;
}
searchView.setOnQueryTextListener(queryTextListener);
return super.onOptionsItemSelected(item);
}
}
Reference screenshot:
https://riptutorial.com/ 1227
https://riptutorial.com/ 1228
, we need understand that it depends completely on the style applied to the underlying Toolbar. To
achieve themeing the Toolbar apply the following steps.
<style name="ActionBarThemeOverlay">
<item name="android:textColorPrimary">@color/prim_color</item>
<item name="colorControlNormal">@color/normal_color</item>
<item name="colorControlHighlight">@color/high_color</item>
<item name="android:textColorHint">@color/hint_color</item>
</style>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
app:theme="@style/ActionBarThemeOverlay"
app:popupTheme="@style/ActionBarThemeOverlay"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:title="@string/title"
tools:targetApi="m" />
This gives the desired color to the all the views corresponding to the Toolbar (back button, Menu
icons and SearchView).
https://riptutorial.com/ 1229
Chapter 212: Secure SharedPreferences
Introduction
Shared Preferences are key-value based XML files. It is located under
/data/data/package_name/shared_prefs/<filename.xml>.
So a user with root privileges can navigate to this location and can change its values. If you want
to protect values in your shared preferences, you can write a simple encryption and decryption
mechanism.
You should know tough, that Shared Preferences were never built to be secure, it's just a simple
way to persist data.
Syntax
1. public static String encrypt(String input);
2. public static String decrypt(String input);
Parameters
Parameter Definition
Remarks
Shared Preferences were never built to be secure, it's just a simple way to persist data.
It is not a good idea to use shared preferences for storing critical information such as user
credentials. To save user credentials (such as passwords) you need to use other methods such as
Android's AccountManager.
Examples
Securing a Shared Preference
Simple Codec
Here to illustrate the working principle we can use simple encryption and decryption as follows.
https://riptutorial.com/ 1230
}
Implementation Technique
// To Write
SharedPreferences preferences = getSharedPreferences(pref_name, MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(encrypt("password"), encrypt("my_dummy_pass"));
editor.apply(); // Or commit if targeting old devices
// To Read
SharedPreferences preferences = getSharedPreferences(pref_name, MODE_PRIVATE);
String passEncrypted = preferences.getString(encrypt("password"), encrypt("default_value"));
String password = decrypt(passEncrypted);
https://riptutorial.com/ 1231
Chapter 213: Secure SharedPreferences
Introduction
Shared Preferences are key-value based XML files. It is located under
/data/data/package_name/shared_prefs/<filename.xml>.
So a user with root privileges can navigate to this location and can change its values. If you want
to protect values in your shared preferences, you can write a simple encryption and decryption
mechanism.
You should know tough, that Shared Preferences were never built to be secure, it's just a simple
way to persist data.
Syntax
1. public static String encrypt(String input);
2. public static String decrypt(String input);
Parameters
Parameter Definition
Remarks
Shared Preferences were never built to be secure, it's just a simple way to persist data.
It is not a good idea to use shared preferences for storing critical information such as user
credentials. To save user credentials (such as passwords) you need to use other methods such as
Android's AccountManager.
Examples
Securing a Shared Preference
Simple Codec
Here to illustrate the working principle we can use simple encryption and decryption as follows.
https://riptutorial.com/ 1232
}
Implementation Technique
// To Write
SharedPreferences preferences = getSharedPreferences(pref_name, MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(encrypt("password"), encrypt("my_dummy_pass"));
editor.apply(); // Or commit if targeting old devices
// To Read
SharedPreferences preferences = getSharedPreferences(pref_name, MODE_PRIVATE);
String passEncrypted = preferences.getString(encrypt("password"), encrypt("default_value"));
String password = decrypt(passEncrypted);
https://riptutorial.com/ 1233
Chapter 214: Security
Examples
Verifying App Signature - Tamper Detection
This technique details how to ensure that your .apk has been signed with your developer
certificate, and leverages the fact that the certificate remains consistent and that only you have
access to it. We can break this technique into 3 simple steps:
try {
PackageInfo packageInfo =
context.getPackageManager().getPackageInfo(context.getPackageName(),
PackageManager.GET_SIGNATURES);
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
//compare signatures
if (SIGNATURE.equals(currentSignature)){
return VALID;
};
}
} catch (Exception e) {
//assumes an issue in checking signature., but we let the caller decide on what to do.
}
return INVALID;
https://riptutorial.com/ 1234
Chapter 215: SensorManager
Examples
Retrieving sensor events
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
accelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
gyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
@Override
public void onResume() {
//Register listeners for your sensors of interest
mSensorManager.registerListener(this, accelerometer,
SensorManager.SENSOR_DELAY_FASTEST);
mSensorManager.registerListener(this, gyroscope, SensorManager.SENSOR_DELAY_FASTEST);
super.onResume();
}
@Override
protected void onPause() {
//Unregister any previously registered listeners
mSensorManager.unregisterListener(this);
super.onPause();
}
@Override
public void onSensorChanged(SensorEvent event) {
//Check the type of sensor data being polled and store into corresponding float array
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
accelerometerData = event.values;
} else if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
gyroscopeData = event.values;
}
}
@Override
https://riptutorial.com/ 1235
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
}
The sensor values returned by Android are with respective to the phone's coordinate system (e.g.
+Y points towards the top of the phone). We can transform these sensor values into a world
coordinate system (e.g. +Y points towards magnetic North, tangential to the ground) using the
sensor managers rotation matrix
First, you would need to declare and initialize the matrices/arrays where data will be stored (you
can do this in the onCreate method, for example):
Next, we need to detect changes in sensor values, store them into the corresponding arrays (if we
want to use them later/elsewhere), then calculate the rotation matrix and resulting transformation
into world coordinates:
if (i == Sensor.TYPE_ACCELEROMETER) {
accelerometerData = event.values;
} else if (i == Sensor.TYPE_GRAVITY) {
gravityData = event.values;
} else if (i == Sensor.TYPE_MAGNETIC) {
magneticData = event.values;
}
https://riptutorial.com/ 1236
SensorManager sensorManager;
Sensor mAccelerometer;
final float movementThreshold = 0.5f; // You may have to change this value.
boolean isMoving = false;
float[] prevValues = {1.0f, 1.0f, 1.0f};
float[] currValues = new float[3];
sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
mAccelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
You may have to adjust the sensitivity by adapting the movementThreshold by trial and error. Then,
override the onSensorChanged() method as follows:
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor == mAccelerometer) {
System.arraycopy(event.values, 0, currValues, 0, event.values.length);
if ((Math.abs(currValues[0] - prevValues[0]) > movementThreshold) ||
(Math.abs(currValues[1] - prevValues[1]) > movementThreshold) ||
(Math.abs(currValues[2] - prevValues[2]) > movementThreshold)) {
isMoving = true;
} else {
isMoving = false;
}
System.arraycopy(currValues, 0, prevValues, 0, currValues.length);
}
}
If you want to prevent your app from being installed on devices that do not have an accelerometer,
you have to add the following line to your manifest:
https://riptutorial.com/ 1237
Chapter 216: Service
Introduction
A Service runs in background to perform long-running operations or to perform work for remote
processes. A service does not provide any user interface it runs only in background with User’s
input. For example a service can play music in the background while the user is in a different App,
or it might download data from the internet without blocking user’s interaction with the Android
device.
Remarks
If you have not defined your service in your AndroidManifest.xml, you will receive a
ServiceNotFoundException when attempting to start it.
Note:
Examples
Starting a Service
Starting a service is very easy, just call startService with an intent, from within an Activity:
Intent intent = new Intent(this, MyService.class); //substitute MyService with the name of
your service
intent.putExtra(Intent.EXTRA_TEXT, "Some text"); //add any extra data to pass to the service
Lifecycle of a Service
• onCreate() :
Executed when the service is first created in order to set up the initial configurations you might
need. This method is executed only if the service is not already running.
• onStartCommand() :
https://riptutorial.com/ 1238
• onBind() :
Executed when a component calls bindService() and returns an instance of IBInder, providing a
communication channel to the Service. A call to bindService() will keep the service running as long
as there are clients bound to it.
• onDestroy() :
Executed when the service is no longer in use and allows for disposal of resources that have been
allocated.
It is important to note that during the lifecycle of a service other callbacks might be invoked such
as onConfigurationChanged() and onLowMemory()
https://developer.android.com/guide/components/services.html
The android:process field defines the name of the process where the service is to run. Normally, all
components of an application run in the default process created for the application. However, a
component can override the default with its own process attribute, allowing you to spread your
application across multiple processes.
https://riptutorial.com/ 1239
If the name assigned to this attribute begins with a colon (':'), the service will run in its own
separate process.
<service
android:name="com.example.appName"
android:process=":externalProcess" />
If the process name begins with a lowercase character, the service will run in a global process of
that name, provided that it has permission to do so. This allows components in different
applications to share a process, reducing resource usage.
Create a class which extends Service class and in overridden method onBind return your local
binder instance:
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
Then in your activity bind to service in onStart callback, using ServiceConnection instance and
unbind from it in onStop:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
https://riptutorial.com/ 1240
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
// IRemoteService.aidl
package com.example.android;
Now after build application, sdk tools will generate appropriate .java file. This file will contain Stub
class which implements our aidl interface, and which we need to extend:
https://riptutorial.com/ 1241
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
Then in activity:
@Override
public void onServiceDisconnected(ComponentName componentName) {}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, RemoteService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(connection);
}
try {
result = service.getPid();
} catch (RemoteException e) {
e.printStackTrace();
}
return result;
}
}
https://riptutorial.com/ 1242
The first thing to do is to add the service to AndroidManifest.xml, inside the <application> tag:
<application ...>
...
<service
android:name=".RecordingService"
<!--"enabled" tag specifies Whether or not the service can be instantiated by the
system — "true" -->
<!--if it can be, and "false" if not. The default value is "true".-->
android:enabled="true"
<!--exported tag specifies Whether or not components of other applications can invoke
the -->
<!--service or interact with it — "true" if they can, and "false" if not. When the
value-->
<!--is "false", only components of the same application or applications with the same
user -->
<!--ID can start the service or bind to it.-->
android:exported="false" />
</application>
android:name=".RecordingService"
to
android:name=".AllServices.RecordingService"
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate(){
instance = this;
isRunning = true;
https://riptutorial.com/ 1243
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId){
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this,
MainActivity.class), 0);
// Set the info for the views that show in the notification panel.
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher) // the status icon
.setTicker("Service running...") // the status text
.setWhen(System.currentTimeMillis()) // the time stamp
.setContentTitle("My App") // the label of the entry
.setContentText("Service running...") // the content of the entry
.setContentIntent(contentIntent) // the intent to send when the
entry is clicked
.setOngoing(true) // make persistent (disable swipe-
away)
.build();
return START_STICKY;
}
@Override
public void onDestroy(){
isRunning = false;
instance = null;
super.onDestroy();
}
All this service does is show a notification when it's running, and it can display toasts when its
doSomething() method is called.
As you'll notice, it's implemented as a singleton, keeping track of its own instance - but without the
usual static singleton factory method because services are naturally singletons and are created by
intents. The instance is useful to the outside to get a "handle" to the service when it's running.
https://riptutorial.com/ 1244
public void startOrStopService(){
if( RecordingService.isRunning ){
// Stop service
Intent intent = new Intent(this, RecordingService.class);
stopService(intent);
}
else {
// Start service
Intent intent = new Intent(this, RecordingService.class);
startService(intent);
}
}
In this example, the service is started and stopped by the same method, depending on it's current
state.
https://riptutorial.com/ 1245
Chapter 217: Shared Element Transitions
Introduction
Here you find examples for transition between Activities or Fragments using a shared element. An
example for this behaviour is the Google Play Store App which translates an App's icon from the
list to the App's details view.
Syntax
• transaction.addSharedElement(sharedElementView, "targetTransitionName");
• fragment.setSharedElementEnterTransition(new CustomTransaction());
Examples
Shared Element Transition between two Fragments
In this example, one of two different ImageViews should be translated from the ChooserFragment to
the DetailFragment.
<ImageView
android:id="@+id/image_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_first"
android:transitionName="fistImage" />
<ImageView
android:id="@+id/image_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_second"
android:transitionName="secondImage" />
In the ChooserFragments class, we need to pass the View which was clicked and an ID to the parent
Activity wich is handling the replacement of the fragments (we need the ID to know which image
resource to show in the DetailFragment). How to pass information to a parent activity in detail is
surely covered in another documentation.
view.findViewById(R.id.image_first).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mCallback != null) {
mCallback.showDetailFragment(view, 1);
}
}
});
https://riptutorial.com/ 1246
view.findViewById(R.id.image_second).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mCallback != null) {
mCallback.showDetailFragment(view, 2);
}
}
});
In the DetailFragment, the ImageView of the shared element also needs the unique transitionName
attribute.
<ImageView
android:id="@+id/image_shared"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:transitionName="sharedImage" />
In the onCreateView() method of the DetailFragment, we have to decide which image resource
should be shown (if we don't do that, the shared element will disappear after the transition).
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_detail, container, false);
case 2:
sharedImage.setBackgroundResource(R.drawable.ic_second);
break;
}
return view;
}
The parent Activity is receiving the callbacks and handles the replacement of the fragments.
https://riptutorial.com/ 1247
@Override
public void showDetailFragment(View sharedElement, int type) {
// Get the chooser fragment, which is shown in the moment.
Fragment chooserFragment = getFragmentManager().findFragmentById(R.id.fragment_container);
// Now use the image's view and the target transitionName to define the shared element.
transaction.addSharedElement(sharedElement, "sharedImage");
Not to forget - the Transition itself. This example moves and scales the shared element.
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class DetailsTransition extends TransitionSet {
public DetailsTransition() {
setOrdering(ORDERING_TOGETHER);
addTransition(new ChangeBounds()).
addTransition(new ChangeTransform()).
addTransition(new ChangeImageTransform());
}
https://riptutorial.com/ 1248
Chapter 218: SharedPreferences
Introduction
SharedPreferences provide a way to save data to disk in the form of key-value pairs.
Syntax
• Context Method
• Activity Method
• SharedPreferences Methods
• SharedPreferences.Editor Methods
https://riptutorial.com/ 1249
Parameters
Parameter Details
All the get functions take a default value, which is returned if the given key is not
defValue present in the SharedPreferences. It's not returned if the key is present but the
value has the wrong type: in that case you get a ClassCastException.
Remarks
• SharedPreferencesshouldn't be used for storing large amount of data. For such purposes, it's
much better to use SQLiteDatabase.
• SharedPreferences are single process only, unless you use deprecated mode
MODE_MULTI_PROCESS. So if your app has multiple processes, you won't be able to read main
process's SharedPreferences in another process. In such cases, you should use another
mechanism to share data across processes, but don't use MODE_MULTI_PROCESS as it is not
reliable as well as deprecated.
• It's better to use SharedPreferences instance in Singleton class to access all over the
Application context. If you want to use it only for particular Activity go for getPreferences().
• Avoid storing sensitive information in clear text while using SharedPreferences since it can be
read easily.
Official Documentation
https://developer.android.com/reference/android/content/SharedPreferences.html
Examples
Read and write values to SharedPreferences
https://riptutorial.com/ 1250
private static final String KEY_FLOAT = "KEY_FOR_YOUR_FLOAT";
private static final String KEY_INT = "KEY_FOR_YOUR_INT";
private static final String KEY_LONG = "KEY_FOR_YOUR_LONG";
@Override
protected void onStart() {
super.onStart();
// Get the saved flag (or default value if it hasn't been saved yet)
SharedPreferences settings = getSharedPreferences(PREFS_FILE, PREFS_MODE);
// read a boolean value (default false)
boolean booleanVal = settings.getBoolean(KEY_BOOLEAN, false);
// read an int value (Default 0)
int intVal = settings.getInt(KEY_INT, 0);
// read a string value (default "my string")
String str = settings.getString(KEY_STRING, "my string");
// read a long value (default 123456)
long longVal = settings.getLong(KEY_LONG, 123456);
// read a float value (default 3.14f)
float floatVal = settings.getFloat(KEY_FLOAT, 3.14f);
}
@Override
protected void onStop() {
super.onStop();
getSharedPreferences() is a method from the Context class — which Activity extends. If you need
to access the getSharedPreferences() method from other classes, you can use
context.getSharedPreferences() with a Context Object reference from an Activity, View, or
Application.
Removing keys
// ...
// ...
https://riptutorial.com/ 1251
SharedPreferences.Editor editor = prefs.edit();
editor.putString(MY_PREF, "value");
editor.remove(MY_PREF);
editor.apply();
After the apply(), prefs contains "key" -> "value", in addition to whatever it contained already. Even
though it looks like I added "key" and then removed it, the remove actually happens first. The
changes in the Editor are all applied in one go, not in the order you added them. All removes
happen before all puts.
One use of SharedPreferences is to implement a "Settings" screen in your app, where the user can
set their preferences / options. Like this:
This goes in /res/xml/preferences.xml, and for the above settings screen, it looks like this:
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="General options">
<CheckBoxPreference
android:key = "silent_mode"
android:defaultValue="false"
https://riptutorial.com/ 1252
android:title="Silent Mode"
android:summary="Mute all sounds from this app" />
<SwitchPreference
android:key="awesome_mode"
android:defaultValue="false"
android:switchTextOn="Yes"
android:switchTextOff="No"
android:title="Awesome mode™"
android:summary="Enable the Awesome Mode™ feature"/>
<EditTextPreference
android:key="custom_storage"
android:defaultValue="/sdcard/data/"
android:title="Custom storage location"
android:summary="Enter the directory path where you want data to be saved. If it
does not exist, it will be created."
android:dialogTitle="Enter directory path (eg. /sdcard/data/ )"/>
</PreferenceCategory>
</PreferenceScreen>
This defines the available options in the settings screen. There are many other types of Preference
listed in the Android Developers documentation on the Preference Class.
Next, we need an Activity to host our Preferences user interface. In this case, it's quite short,
and looks like this:
package com.example.preferences;
import android.preference.PreferenceActivity;
import android.os.Bundle;
It extends PreferenceActivity, and provides the user interface for the preferences screen. It can be
started just like a normal activity, in this case, with something like:
Getting the values of the preferences inside your app is quite simple, just call
setDefaultValues() first, in order to set the default values defined in your XML, and then get the
default SharedPreferences. An example:
https://riptutorial.com/ 1253
//get the values of the settings options
boolean silentMode = preferences.getBoolean("silent_mode", false);
boolean awesomeMode = preferences.getBoolean("awesome_mode", false);
The getAll() method retrieves all values from the preferences. We can use it, for instance, to log
the current content of the SharedPreferences:
The documentation warns you about modifying the Collection returned by getAll:
Note that you must not modify the collection returned by this method, or alter any of its
contents. The consistency of your stored data is not guaranteed if you do.
Please note:
• The listener will fire only if value was added or changed, setting the same value won't call it;
• The listener needs to be saved in a member variable and NOT with an anonymous class,
because registerOnSharedPreferenceChangeListener stores it with a weak reference, so it
would be garbage collected;
• Instead of using a member variable, it can also be directly implemented by the class and
then call registerOnSharedPreferenceChangeListener(this);
• Remember to unregister the listener when it is no more required using
https://riptutorial.com/ 1254
unregisterOnSharedPreferenceChangeListener.
SharedPreferences Manager (Singleton) class to read and write all types of data.
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import com.google.gson.Gson;
import java.lang.reflect.Type;
/**
* Singleton Class for accessing SharedPreferences,
* should be initialized once in the beginning by any application component using static
* method initialize(applicationContext)
*/
public class SharedPrefsManager {
/**
* Throws IllegalStateException if this class is not initialized
*
* @return unique SharedPrefsManager instance
*/
public static SharedPrefsManager getInstance() {
if (uniqueInstance == null) {
throw new IllegalStateException(
"SharedPrefsManager is not initialized, call
initialize(applicationContext) " +
"static method first");
}
return uniqueInstance;
}
/**
* Initialize this class using application Context,
* should be called once in the beginning by any application Component
*
* @param appContext application context
*/
public static void initialize(Context appContext) {
if (appContext == null) {
throw new NullPointerException("Provided application context is null");
}
if (uniqueInstance == null) {
synchronized (SharedPrefsManager.class) {
if (uniqueInstance == null) {
uniqueInstance = new SharedPrefsManager(appContext);
https://riptutorial.com/ 1255
}
}
}
}
/**
* Clears all data in SharedPreferences
*/
public void clearPrefs() {
SharedPreferences.Editor editor = getPrefs().edit();
editor.clear();
editor.commit();
}
https://riptutorial.com/ 1256
public void setLong(String key, long value) {
SharedPreferences.Editor editor = getPrefs().edit();
editor.putLong(key, value);
editor.apply();
}
/**
* Persists an Object in prefs at the specified key, class of given Object must implement
Model
* interface
*
* @param key String
* @param modelObject Object to persist
* @param <M> Generic for Object
*/
public <M extends Model> void setObject(String key, M modelObject) {
String value = createJSONStringFromObject(modelObject);
SharedPreferences.Editor editor = getPrefs().edit();
editor.putString(key, value);
editor.apply();
}
/**
* Fetches the previously stored Object of given Class from prefs
*
* @param key String
* @param classOfModelObject Class of persisted Object
* @param <M> Generic for Object
* @return Object of given class
*/
public <M extends Model> M getObject(String key, Class<M> classOfModelObject) {
String jsonData = getPrefs().getString(key, null);
https://riptutorial.com/ 1257
if (null != jsonData) {
try {
Gson gson = new Gson();
M customObject = gson.fromJson(jsonData, classOfModelObject);
return customObject;
} catch (ClassCastException cce) {
Log.d(TAG, "Cannot convert string obtained from prefs into collection of type
" +
classOfModelObject.getName() + "\n" + cce.getMessage());
}
}
return null;
}
/**
* Persists a Collection object in prefs at the specified key
*
* @param key String
* @param dataCollection Collection Object
* @param <C> Generic for Collection object
*/
public <C> void setCollection(String key, C dataCollection) {
SharedPreferences.Editor editor = getPrefs().edit();
String value = createJSONStringFromObject(dataCollection);
editor.putString(key, value);
editor.apply();
}
/**
* Fetches the previously stored Collection Object of given type from prefs
*
* @param key String
* @param typeOfC Type of Collection Object
* @param <C> Generic for Collection Object
* @return Collection Object which can be casted
*/
public <C> C getCollection(String key, Type typeOfC) {
String jsonData = getPrefs().getString(key, null);
if (null != jsonData) {
try {
Gson gson = new Gson();
C arrFromPrefs = gson.fromJson(jsonData, typeOfC);
return arrFromPrefs;
} catch (ClassCastException cce) {
Log.d(TAG, "Cannot convert string obtained from prefs into collection of type
" +
typeOfC.toString() + "\n" + cce.getMessage());
}
}
return null;
}
SharedPreferences.OnSharedPreferenceChangeListener listener) {
getPrefs().unregisterOnSharedPreferenceChangeListener(listener);
https://riptutorial.com/ 1258
}
Model interface which is implemented by classes going to Gson to avoid proguard obfuscation.
import android.preference.PreferenceManager;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
returns the preferences saved by Activity's class name as described in the docs :
Retrieve a SharedPreferences object for accessing preferences that are private to this
https://riptutorial.com/ 1259
activity. This simply calls the underlying getSharedPreferences(String, int) method by
passing in this activity's class name as the preferences name.
While using getSharedPreferences (String name, int mode) method returns the prefs saved under
the given name. As in the docs :
Retrieve and hold the contents of the preferences file 'name', returning a
SharedPreferences through which you can retrieve and modify its values.
So if the value being saved in the SharedPreferences has to be used across the app, one should
use getSharedPreferences (String name, int mode) with a fixed name. As, using getPreferences(int)
returns/saves the preferences belonging to the Activity calling it.
2.3
apply() was added in 2.3 (API 9), it commits without returning a boolean indicating success or
failure.
apply() was added as the Android dev team noticed that almost no one took notice of the return
value, so apply is faster as it is asynchronous.
Unlike commit(), which writes its preferences out to persistent storage synchronously, apply()
commits its changes to the in-memory SharedPreferences immediately but starts an asynchronous
commit to disk and you won't be notified of any failures. If another editor on this SharedPreferences
does a regular commit() while a apply() is still outstanding, the commit() will block until all async
commits(apply) are completed as well as any other sync commits that may be pending.
SharedPreferences allows you to store primitive data types only (boolean, float, long, int, String, and
https://riptutorial.com/ 1260
string set). You cannot store more complex objects in SharedPreferences, and as such is really
meant to be a place to store user settings or similar, it's not meant to be a database to keep user
data (like saving a todo list a user made for example).
To store something in SharedPreferences you use a Key and a Value. The Key is how you can
reference what you stored later and the Value data you want to store.
If value for key not exist then return second param value(In this case null, this is like default value)
https://riptutorial.com/ 1261
Clear all data from SharedPreferences
editor.clear();
editor.commit(); // commit changes
try {
HashSet<String> set = new HashSet<>();
JSONArray json = new JSONArray(input);
for (int i = 0, size = json.length(); i < size; i++) {
String value = json.getString(i);
set.add(value);
}
return set;
} catch (JSONException e) {
e.printStackTrace();
return defaultReturnValue;
}
}
https://riptutorial.com/ 1262
private static void putStringSetToJson(SharedPreferences.Editor editor, String key,
Set<String> values) {
JSONArray json = new JSONArray(values);
if (Build.VERSION.SDK_INT >= 9)
editor.putString(key, json.toString()).apply();
else
editor.putString(key, json.toString()).commit();
}
private SharedPreferencesCompat() {}
}
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int
dstart, int dend) {
try {
int input = Integer.parseInt(dest.toString() + source.toString());
if (isInRange(min, max, input))
return null;
} catch (NumberFormatException nfe) { }
return "";
}
https://riptutorial.com/ 1263
private boolean isInRange(int a, int b, int c) {
return b > a ? c >= a && c <= b : c >= b && c <= a;
}
}
Use :
https://riptutorial.com/ 1264
Chapter 219: ShortcutManager
Examples
Dynamic Launcher Shortcuts
shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));
shortcutManager.removeAllDynamicShortcuts();
shortcutManager.updateShortcuts(Arrays.asList(shortcut);
Please note that setDynamicShortcuts(List)is used to redefine the entire list of dynamic shortcuts,
addDynamicShortcuts(List) is used to add dynamic shortcuts to existing list of dynamic shortcuts
https://riptutorial.com/ 1265
Chapter 220: Sign your Android App for
Release
Introduction
Android requires that all APKs be signed for release.
Examples
Sign your App
2. Select the module you would like to release from the drop down and click Next.
3. To Create a new keystore, click Create new. Now fill the required information and press ok in
New Key Store.
https://riptutorial.com/ 1266
4. On the Generate Signed APK Wizard fields are already populated for you if you just created
new key store otherwise fill it and click next.
5. On the next window, select a destination for the signed APK, select the build type and click
finish.
You can define the signing configuration to sign the apk in the build.gradle file.
android {
signingConfigs {
myConfig {
storeFile file("myFile.keystore")
storePassword "xxxx"
keyAlias "xxxx"
keyPassword "xxxx"
}
}
//....
}
https://riptutorial.com/ 1267
android {
buildTypes {
release {
signingConfig signingConfigs.myConfig
}
}
}
https://riptutorial.com/ 1268
Chapter 221: Smartcard
Examples
Smart card send and receive
Now you have to understand that in java the communication takes place using package
javax.smarcard which is not available for Android so take a look here for getting an idea as to how
you can communicate or send/receive APDU (smartcard command).
You cannot simply send an APDU (smartcard command) over the bulk-out endpoint and expect to
receive a response APDU over the bulk-in endpoint. For getting the endpoints see the code
snippet below :
if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {
// from host to device
epOut = ep;
https://riptutorial.com/ 1269
}
}
Now you have the bulk-in and bulk-out endpoints to send and receive APDU command and APDU
response blocks:
And for receive/ read a response see the code snippet below :
return byteCount;
}
Now if you see this answer here the 1st command to be sent is :
which you can create by reading section 6.1.1 of the USB Device Class Specifications doc here.
Now let's take an example of this command like the one here: 62000000000000000000 How you can
send this is :
Now after you have successfully sent the APDU command, you can read the response using :
read(connection, epIn);
https://riptutorial.com/ 1270
And receive something like
80 18000000 00 00 00 00 00 3BBF11008131FE45455041000000000000000000000000F1
Now the response received in the code here will be in the result variable of read() method from
code
https://riptutorial.com/ 1271
Chapter 222: Snackbar
Syntax
• Snackbar make (View view, CharSequence text, int duration)
• Snackbar make (View view, int resId, int duration)
Parameters
Parameter Description
resId int: The resource id of the string resource to use. Can be formatted text.
Remarks
Snackbar provides lightweight feedback about an operation. It displays a brief message at the
bottom of the screen on mobile and at the bottom left on larger devices. Snackbars appear above
all other elements on the screen and only one can be displayed at a time.
They automatically disappear after a timeout or after user interaction elsewhere on the screen,
particularly after interactions that summon a new surface or activity. Snackbar can be swiped off
screen.
Before using SnackBar you must add the design support library dependency in the build.gradle file:
dependencies {
compile 'com.android.support:design:25.3.1'
}
Official Documentation
https://developer.android.com/reference/android/support/design/widget/Snackbar.html
Examples
Creating a simple Snackbar
https://riptutorial.com/ 1272
Creating a Snackbar can be done as follows:
The view is used to find a suitable parent to use to display the Snackbar. Typically this would be a
CoordinatorLayout that you've defined in your XML, which enables adding functionality such as
swipe to dismiss and automatically moving of other widgets (e.g. FloatingActionButton). If there's
no CoordinatorLayout then the window decor's content view is used.
Very often we also add an action to the Snackbar. A common use case would be an "Undo" action.
}
})
.show();
By default Snackbar dismisses on it's right swipe.This example demonstrates how to dismiss the
snackBar on it's left swipe.
https://riptutorial.com/ 1273
text.setTextColor(context.getResources().getColor(R.color.white));
Typeface font = null;
//Setting font
font = Typeface.createFromAsset(context.getAssets(), "DroidSansFallbackanmol256.ttf");
text.setTypeface(font);
return snackbar;
You can use Snackbar.Callback to listen if the snackbar was dismissed by user or timeout.
@Override
public void onShown(Snackbar snackbar) {
Toast.makeText(getActivity(), "This is my annoying step-brother",
Toast.LENGTH_LONG).show();
}
}).setAction("Go!", new View.OnClickListener() {
@Override
public void onClick(View v) {
}
}).show();
Custom Snackbar
https://riptutorial.com/ 1274
}
});
Toasts are generally used when we want to display an information to the user regarding some
action that has successfully (or not) happened and this action does not require the user to take
any other action. Like when a message has been sent, for example:
Snackbars are also used to display an information. But this time, we can give the user an
opportunity to take an action. For example, let's say the user deleted a picture by mistake and he
wants to get it back. We can provide a Snackbar with the "Undo" action. Like this:
Conclusion: Toasts are used when we don't need user interaction. Snackbars are used to allow
users to take another action or undo a previous one.
Creating an Snackbar without the need pass view to Snackbar, all layout create in android in
android.R.id.content.
https://riptutorial.com/ 1275
public class CustomSnackBar {
switch (actionType) {
case STATE_ERROR:
snackbar.getView().setBackgroundColor(Color.parseColor("#F12B2B"));
break;
case STATE_WARNING:
snackbar.getView().setBackgroundColor(Color.parseColor("#000000"));
break;
case STATE_SUCCESS:
snackbar.getView().setBackgroundColor(Color.parseColor("#7ED321"));
break;
}
snackbar.show();
}
}
new CustomSnackBar(findViewById(CustomSnackBar.VIEW_PARENT),"message",
CustomSnackBar.STATE_ERROR);
https://riptutorial.com/ 1276
Chapter 223: SpannableString
Syntax
• char charAt (int i)
• boolean equals (Object o)
• void getChars (int start, int end, char[] dest, int off)
• int getSpanEnd (Object what)
• int getSpanFlags (Object what)
• int getSpanStart (Object what)
• T[] getSpans (int queryStart, int queryEnd, Class<T> kind)
• int hashCode ()
• int length ()
• int nextSpanTransition (int start, int limit, Class kind)
• void removeSpan (Object what)
• void setSpan (Object what, int start, int end, int flags)
• CharSequence subSequence (int start, int end)
• String toString ()
• SpannableString valueOf (CharSequence source)
Examples
Add styles to a TextView
The TextView will use a SpannableString as its content, which will illustrate some of the available
styles.
• Make it larger
• Bold
• Underline
• Italicize
• Strike-through
• Colored
• Highlighted
• Show as superscript
• Show as subscript
• Show as a link
• Make it clickable.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SpannableString styledString
https://riptutorial.com/ 1277
= new SpannableString("Large\n\n" // index 0 - 5
+ "Bold\n\n" // index 7 - 11
+ "Underlined\n\n" // index 13 - 23
+ "Italic\n\n" // index 25 - 31
+ "Strikethrough\n\n" // index 33 - 46
+ "Colored\n\n" // index 48 - 55
+ "Highlighted\n\n" // index 57 - 68
+ "K Superscript\n\n" // "Superscript" index 72 - 83
+ "K Subscript\n\n" // "Subscript" index 87 - 96
+ "Url\n\n" // index 98 - 101
+ "Clickable\n\n"); // index 103 - 112
// underline text
styledString.setSpan(new UnderlineSpan(), 13, 23, 0);
// highlight text
styledString.setSpan(new BackgroundColorSpan(Color.CYAN), 57, 68, 0);
// superscript
styledString.setSpan(new SuperscriptSpan(), 72, 83, 0);
// make the superscript text smaller
styledString.setSpan(new RelativeSizeSpan(0.5f), 72, 83, 0);
// subscript
styledString.setSpan(new SubscriptSpan(), 87, 96, 0);
// make the subscript text smaller
styledString.setSpan(new RelativeSizeSpan(0.5f), 87, 96, 0);
// url
styledString.setSpan(new URLSpan("http://www.google.com"), 98, 101, 0);
// clickable text
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(View widget) {
// We display a Toast. You could do anything you want here.
Toast.makeText(SpanExample.this, "Clicked", Toast.LENGTH_SHORT).show();
}
};
https://riptutorial.com/ 1278
// this step is mandated for the url and clickable styles.
textView.setMovementMethod(LinkMovementMethod.getInstance());
// make it neat
textView.setGravity(Gravity.CENTER);
textView.setBackgroundColor(Color.WHITE);
textView.setText(styledString);
setContentView(textView);
https://riptutorial.com/ 1279
Multi string , with multi color
Method: setSpanColor
https://riptutorial.com/ 1280
return ss;
}
Usage:
String a = getString(R.string.string1);
String b = getString(R.string.string2);
https://riptutorial.com/ 1281
Chapter 224: Speech to Text Conversion
Examples
Speech to Text With Default Google Prompt Dialog
//Intent to listen to user vocal input and return result in same activity
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQ_CODE_SPEECH_INPUT: {
if (resultCode == RESULT_OK && null != data) {
}
}
Output
https://riptutorial.com/ 1282
Speech to Text without Dialog
The following code can be used to trigger speech-to-text translation without showing a dialog:
The custom listener class CustomRecognitionListener used in the code above is implemented as
follows:
https://riptutorial.com/ 1283
Log.d(TAG, "onEndofSpeech");
}
conversionCallaback.onErrorOccured(TranslatorUtil.getErrorText(error));
}
https://riptutorial.com/ 1284
Chapter 225: Spinner
Examples
Adding a spinner to your activity
In /res/values/strings.xml:
<string-array name="spinner_options">
<item>Option 1</item>
<item>Option 2</item>
<item>Option 3</item>
</string-array>
In layout XML:
<Spinner
android:id="@+id/spinnerName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/spinner_options" />
In Activity:
<Spinner
android:id="@+id/spinner" <!-- id to refer this spinner from JAVA-->
android:layout_width="match_parent"
android:layout_height="wrap_content">
</Spinner>
Now Secondly populate values in spinner There are mainly two ways to populate values in spinner
.
1. From XML itself create a array.xml in values directory under res. Create this array
https://riptutorial.com/ 1285
<string-array name="defaultValue">
<item>--Select City Area--</item>
<item>--Select City Area--</item>
<item>--Select City Area--</item>
</string-array>
android:entries="@array/defaultValue"
if you are using in activity cityArea = (Spinner) findViewById(R.id.cityArea); else if you are using
in fragment
If an app does not explicitly request a theme in its manifest, Android System will determine the
default theme based on the app’s targetSdkVersion to maintain the app’s original expectations:
https://riptutorial.com/ 1286
Spinner can be easily customized with the help of xml eg
android:background="@drawable/spinner_background"
android:layout_margin="16dp"
android:padding="16dp"
easily get the position and other details of the selected item in spinner
cityArea.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
areaNo = position;
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long
id) {
((TextView) parent.getChildAt(0)).setTextColor(0x00000000);
// similarly change `background color` etc.
}
https://riptutorial.com/ 1287
Chapter 226: Split Screen / Multi-Screen
Activities
Examples
Split Screen introduced in Android Nougat implemented.
Set this attribute in your manifest's or element to enable or disable multi-window display:
android:resizeableActivity=["true" | "false"]
If this attribute is set to true, the activity can be launched in split-screen and freeform modes. If the
attribute is set to false, the activity does not support multi-window mode. If this value is false, and
the user attempts to launch the activity in multi-window mode, the activity takes over the full
screen.
If your app targets API level 24, but you do not specify a value for this attribute, the attribute's
value defaults to true.
The following code shows how to specify an activity's default size and location, and its minimum
size, when the activity is displayed in freeform mode:
Certain features are disabled or ignored when a device is in multi-window mode, because they
don’t make sense for an activity which may be sharing the device screen with other activities or
apps. Such features include:
1. Some System UI customization options are disabled; for example, apps cannot hide the
status bar if they are not running in full-screen mode.
If your app targets API level 23 or lower and the user attempts to use the app in multi-window
mode, the system forcibly resizes the app unless the app declares a fixed orientation.
https://riptutorial.com/ 1288
If your app does not declare a fixed orientation, you should launch your app on a device running
Android 7.0 or higher and attempt to put the app in split-screen mode. Verify that the user
experience is acceptable when the app is forcibly resized.
If the app declares a fixed orientation, you should attempt to put the app in multi-window mode.
Verify that when you do so, the app remains in full-screen mode.
https://riptutorial.com/ 1289
Chapter 227: SQLite
Introduction
SQLite is a relational database management system written in C. To begin working with SQLite
databases within the Android framework, define a class that extends SQLiteOpenHelper, and
customize as needed.
Remarks
The SQLiteOpenHelper class defines static onCreate() and onUpgrade() methods. These methods are
called in the corresponding methods of a SQLiteOpenHelper subclass that you customize with your
own tables.
Examples
Using the SQLiteOpenHelper class
https://riptutorial.com/ 1290
COLUMN_ID + ")" +
");";
@Override
public void onCreate(SQLiteDatabase db) {
// onCreate should always create your most up to date database
// This method is called when the app is newly installed
db.execSQL(CREATE_TABLE_PRODUCT);
db.execSQL(CREATE_TABLE_TRANSACTION);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// onUpgrade is responsible for upgrading the database when you make
// changes to the schema. For each version the specific changes you made
// in that version have to be applied.
for (int version = oldVersion + 1; version <= newVersion; version++) {
switch (version) {
case 2:
db.execSQL("ALTER TABLE " + TABLE_PRODUCTS + " ADD COLUMN " +
COLUMN_DESCRIPTION + " TEXT;");
break;
case 3:
db.execSQL(CREATE_TABLE_TRANSACTION);
break;
}
}
}
}
// Create a ContentValues instance which contains the data for each column
// You do not need to specify a value for the PRIMARY KEY column.
// Unique values for these are automatically generated.
final ContentValues values = new ContentValues();
values.put(COLUMN_NAME, model.getName());
values.put(COLUMN_DESCRIPTION, model.getDescription());
values.put(COLUMN_VALUE, model.getValue());
https://riptutorial.com/ 1291
values // The ContentValues instance which contains the data
);
onUpgrade() method
In this class, the onUpgrade() method is responsible for upgrading the database when you make
changes to the schema. It is called when the database file already exists, but its version is lower
than the one specified in the current version of the app. For each database version, the specific
changes you made have to be applied.
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Loop through each version when an upgrade occurs.
for (int version = oldVersion + 1; version <= newVersion; version++) {
switch (version) {
case 2:
// Apply changes made in version 2
db.execSQL(
"ALTER TABLE " +
TABLE_PRODUCTS +
" ADD COLUMN " +
COLUMN_DESCRIPTION +
" TEXT;"
);
break;
case 3:
// Apply changes made in version 3
db.execSQL(CREATE_TABLE_TRANSACTION);
break;
}
}
}
Here is an example of a method that would live inside a SQLiteOpenHelper subclass. It uses the
searchTerm String to filter the results, iterates through the Cursor's contents, and returns those
contents in a List of Product Objects.
First, define the Product POJO class that will be the container for each row retrieved from the
database:
https://riptutorial.com/ 1292
mDescription = description;
mValue = value;
}
}
Then, define the method that will query the database, and return a List of Product Objects:
// When reading data one should always just get a readable database.
final SQLiteDatabase database = this.getReadableDatabase();
// Having clause. When using the GroupBy clause this allows you to
// specify which groups to include.
null,
// To increase performance first get the index of each column in the cursor
final int idIndex = cursor.getColumnIndex(COLUMN_ID);
final int nameIndex = cursor.getColumnIndex(COLUMN_NAME);
final int descriptionIndex = cursor.getColumnIndex(COLUMN_DESCRIPTION);
final int valueIndex = cursor.getColumnIndex(COLUMN_VALUE);
try {
do {
// Read the values of a row in the table using the indexes acquired above
final long id = cursor.getLong(idIndex);
https://riptutorial.com/ 1293
final String name = cursor.getString(nameIndex);
final String description = cursor.getString(descriptionIndex);
final float value = cursor.getFloat(valueIndex);
} while (cursor.moveToNext());
return products;
} finally {
// Don't forget to close the Cursor once you are done to avoid memory leaks.
// Using a try/finally like in this example is usually the best way to handle this
cursor.close();
DBContract.java
//Use CONTENT_AUTHORITY to create all the database URI's that the app will use to link the
content provider.
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
/*the name of the uri that can be the same as the name of your table.
this will translate to content://com.yourdomain.yourapp/user/ as a valid URI
*/
public static final String PATH_USER = "User";
https://riptutorial.com/ 1294
public static Uri buildUri(long id){
return ContentUris.withAppendedId(CONTENT_URI,id);
}
}
DBHelper.java
//if you change the schema of the database, you must increment this number
private static final int DATABASE_VERSION=1;
static final String DATABASE_NAME="mydatabase.db";
private static DBHelper mInstance=null;
public static DBHelper getInstance(Context ctx){
if(mInstance==null){
mInstance= new DBHelper(ctx.getApplicationContext());
}
return mInstance;
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase){
//Create the table users
final String SQL_CREATE_TABLE_USERS="CREATE TABLE "+UserEntry.TABLE_NAME+ " ("+
UserEntry._ID+" INTEGER PRIMARY KEY, "+
UserEntry.COLUMN_Name+" TEXT , "+
UserEntry.COLUMN_Password+" TEXT "+
" ); ";
sqLiteDatabase.execSQL(SQL_CREATE_TABLE_USERS);
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + UserEntry.TABLE_NAME);
}
DBProvider.java
https://riptutorial.com/ 1295
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
final String authority = DBContract.CONTENT_AUTHORITY;
return matcher;
}
@Override
public boolean onCreate() {
mDBHelper = new DBHelper(getContext());
return false;
}
@Override
public String getType(Uri uri) {
// determine what type of Uri is
final int match = sUriMatcher.match(uri);
switch (match) {
case USER:
return DBContract.UserEntry.CONTENT_TYPE;
default:
throw new UnsupportedOperationException("Uri unknown: " + uri);
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[]
selectionArgs,
String sortOrder) {
Cursor retCursor;
try {
switch (sUriMatcher.match(uri)) {
case USER: {
retCursor = mDBHelper.getReadableDatabase().query(
DBContract.UserEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
break;
}
default:
throw new UnsupportedOperationException("Uri unknown: " + uri);
}
} catch (Exception ex) {
Log.e("Cursor", ex.toString());
} finally {
mDBHelper.close();
}
https://riptutorial.com/ 1296
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mDBHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
Uri returnUri;
try {
switch (match) {
case USER: {
long _id = db.insert(DBContract.UserEntry.TABLE_NAME, null, values);
if (_id > 0)
returnUri = DBContract.UserEntry.buildUri(_id);
else
throw new android.database.SQLException("Error at inserting row in " +
uri);
break;
}
default:
throw new UnsupportedOperationException("Uri unknown: " + uri);
}
mContext.getContentResolver().notifyChange(uri, null);
return returnUri;
} catch (Exception ex) {
Log.e("Insert", ex.toString());
db.close();
} finally {
db.close();
}
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
final SQLiteDatabase db = DBHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
int deletedRows;
if (null == selection) selection = "1";
try {
switch (match) {
case USER:
deletedRows = db.delete(
DBContract.UserEntry.TABLE_NAME, selection, selectionArgs);
break;
default:
throw new UnsupportedOperationException("Uri unknown: " + uri);
}
if (deletedRows != 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return deletedRows;
} catch (Exception ex) {
Log.e("Insert", ex.toString());
} finally {
db.close();
}
return 0;
}
https://riptutorial.com/ 1297
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
{
final SQLiteDatabase db = mDBHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
int updatedRows;
try {
switch (match) {
case USER:
updatedRows = db.update(DBContract.UserEntry.TABLE_NAME, values,
selection, selectionArgs);
break;
default:
throw new UnsupportedOperationException("Uri unknown: " + uri);
}
if (updatedRows != 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return updatedRows;
} catch (Exception ex) {
Log.e("Update", ex.toString());
} finally {
db.close();
}
return -1;
}
How to Use:
// Create a ContentValues instance which contains the up to date data for each column
// Unlike when inserting data you need to specify the value for the PRIMARY KEY column as well
final ContentValues values = new ContentValues();
values.put(COLUMN_ID, model.getId());
https://riptutorial.com/ 1298
values.put(COLUMN_NAME, model.getName());
values.put(COLUMN_DESCRIPTION, model.getDescription());
values.put(COLUMN_VALUE, model.getValue());
Performing a Transaction
Transactions can be used to make multiple changes to the database atomically. Any normal
transaction follows this pattern:
https://riptutorial.com/ 1299
db.close();
To delete all rows from the table and get the count of the deleted row in return value
// Database Name
private static final String DATABASE_NAME = "database_name";
// Table Names
private static final String DB_TABLE = "table_image";
// column names
private static final String KEY_NAME = "image_name";
private static final String KEY_IMAGE = "image_data";
@Override
public void onCreate(SQLiteDatabase db) {
// creating table
https://riptutorial.com/ 1300
db.execSQL(CREATE_TABLE_IMAGE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// on upgrade drop older tables
db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);
Retrieving data:
Note:
1. Before inserting into database, you need to convert your Bitmap image into byte array first
then apply it using database query.
2. When retrieving from database, you certainly have a byte array of image, what you need to
do is to convert byte array back to original image. So, you have to make use of
BitmapFactory to decode.
https://riptutorial.com/ 1301
Put your dbname.sqlite or dbname.db file in assets folder of your project.
/**
* Creates a empty database on the system and rewrites it with your own database.
*/
public void createDataBase() throws IOException {
boolean dbExist = checkDataBase();
if (dbExist) {
//do nothing - database already exist
} else {
//By calling this method and empty database will be created into the default
system path
//of your application so we are gonna be able to overwrite that database with
our database.
this.getReadableDatabase();
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
/**
* Check if the database already exist to avoid re-copying the file each time you open
the application.
*
* @return true if it exists, false if it doesn't
*/
private boolean checkDataBase() {
SQLiteDatabase checkDB = null;
try {
checkDB = SQLiteDatabase.openDatabase(outFileName, null,
SQLiteDatabase.OPEN_READWRITE);
} catch (SQLiteException e) {
try {
copyDataBase();
https://riptutorial.com/ 1302
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (checkDB != null) {
checkDB.close();
}
return checkDB != null ? true : false;
}
/**
* Copies your database from your local assets-folder to the just created empty
database in the
* system folder, from where it can be accessed and handled.
* This is done by transfering bytestream.
*/
Log.i("Database",
"New database is being copied to device!");
byte[] buffer = new byte[1024];
OutputStream myOutput = null;
int length;
// Open your local db as the input stream
InputStream myInput = null;
try {
myInput = myContext.getAssets().open(DB_NAME);
// transfer bytes from the inputfile to the
// outputfile
myOutput = new FileOutputStream(DB_PATH + DB_NAME);
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
myOutput.close();
myOutput.flush();
myInput.close();
Log.i("Database",
"New database has been copied to device!");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public synchronized void close() {
if (db != null)
db.close();
super.close();
}
https://riptutorial.com/ 1303
}
@Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
}
}
Then in onCreate Method initialize it and call createDatabase() Method as show below.
db = new Databasehelper(MainActivity.this);
try {
db.createDataBase();
} catch (Exception e) {
e.printStackTrace();
}
Perform all of your insert, update, delete and select operation as shown below.
You might want to import and export your database for bacukups for example. Dont forget about
the permissions.
Toast.makeText(c, c.getResources().getString(R.string.exporterenToast),
Toast.LENGTH_SHORT).show();
}
catch (Exception e) {
https://riptutorial.com/ 1304
Toast.makeText(c, c.getResources().getString(R.string.portError),
Toast.LENGTH_SHORT).show();
Log.d("Main", e.toString());
}
}
Bulk insert
Here is an example of inserting large chunks of data at once. All the data you want to insert is
gathered inside of a ContentValues array.
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
int count = 0;
String table = null;
https://riptutorial.com/ 1305
} finally {
mDatabase.endTransaction();
}
return count;
}
https://riptutorial.com/ 1306
Chapter 228: Storing Files in Internal &
External Storage
Syntax
• FileOutputStream openFileInput (String name)
• FileOutputStream openFileOutput (String name, int mode)
• File(File dir, String name)
• File(String path)
• File getExternalStoragePublicDirectory (String type)
• File getExternalFilesDir (String type)
Parameters
Parameter Details
name The name of the file to open. NOTE: Cannot contain path separators
Examples
Using Internal Storage
By default, any files that you save to Internal Storage are private to your application. They cannot
be accessed by other applications, nor the user under normal circumstances. These files are
deleted when the user uninstalls the application.
https://riptutorial.com/ 1307
try {
fileOutputStream = openFileOutput(fileName, Context.MODE_PRIVATE);
fileOutputStream.write(textToWrite.getBytes());
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
"External" Storage is another type of storage that we can use to save files to the user's device. It
has some key differences from "Internal" Storage, namely:
• It is not always available. In the case of a removable medium (SD card), the user can simply
remove the storage.
• It is not private. The user (and other applications) have access to these files.
• If the user uninstalls the app, the files you save in the directory retrieved with
getExternalFilesDir() will be removed.
To use External Storage, we need to first obtain the proper permissions. You will need to use:
To grant these permissions, you will need to identify them in your AndroidManifest.xml as such
NOTE: Since they are Dangerous permissions if you are using API Level 23 or above,
you will need to request the permissions at runtime.
Before attempting to write or read from External Storage, you should always check that the
storage medium is available.
When writing files to the External Storage, you should decide if the file should be recognized as
Public or Private. While both of these types of files are still accessible to the user and other
https://riptutorial.com/ 1308
applications on the device, there is a key distinction between them.
Public files should remain on the device when the user uninstalls the app. An example of a file that
should be saved as Public would be photos that are taken through your application.
Private files should all be removed when the user uninstalls the app. These types of files would be
app specific, and not be of use to the user or other applications. Ex. temporary files
downloaded/used by your application.
Here's how to get access to the Documents directory for both Public and Private files.
Public
Private
Android developers(mainly beginners) have been confused regarding Internal & External storage
terminology. There are lot of questions on Stackoverflow regarding the same. This is mainly
because of the fact that terminology according to Google/official Android documentation is quite
different to that of normal Android OS user. Hence I thought documenting this would help.
Internal storage(GT):
By default, files saved to the internal storage are private to your application and other
https://riptutorial.com/ 1309
applications cannot access them (nor can the user).
External storage(GT):
This is same as phone’s inbuilt internal This is same as removable micro SD card
memory (or) Internal storage(UT) storage (or) External storage(UT)
This type of storage can be accessed on This type of storage can be accessed on
windows PC by connecting your phone to PC windows PC by connecting your phone to
via USB cable and selecting Camera(PTP) in PC via USB cable and selecting File transfer
the USB options notification. in the USB options notification.
In a nutshell,
Internal Storage(GT): By default, files saved to the internal storage are private to your application
and other applications cannot access them. Your app user also can't access them using file
manager; even after enabling "show hidden files" option in file manager. To access files in Internal
Storage(GT), you have to root your Android phone. Moreover, when the user uninstalls your
application, these files are removed/deleted.
So Internal Storage(GT) is NOT what we think as Nexus 6P's 32/64 GB internal memory
External Storage(GT):
Every Android-compatible device supports a shared "external storage" that you can
use to save files. Files saved to the external storage are world-readable and can be
modified by the user when they enable USB mass storage to transfer files on a
https://riptutorial.com/ 1310
computer.
In order to read or write files on the External Storage(GT), your app must acquire the
READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE system permissions.
For example:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
If you need to both read and write files, then you need to request only the
WRITE_EXTERNAL_STORAGE permission, because it implicitly requires read access as well.
In External Storage(GT), you may also save files that are app-private
But,
When the user uninstalls your application, this directory and all its contents are deleted.
When do you need to save files that are app-private in External Storage(GT)?
If you are handling files that are not intended for other apps to use (such as graphic
textures or sound effects used by only your app), you should use a private storage
directory on the external storage
Beginning with Android 4.4, reading or writing files in your app's private directories
does not require the READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE permissions. So
you can declare the permission should be requested only on the lower versions of
Android by adding the maxSdkVersion attribute:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
</manifest
File getFilesDir ()
https://riptutorial.com/ 1311
File getExternalStorageDirectory ()
Before API level 19, there was no official way to store in SD card. But, many could do it using
unofficial libraries or APIs.
Officially, one method was introduced in Context class in API level 19 (Android version 4.4 - Kitkat).
That means, it will return paths to both types of External Storage(GT) - Internal memory and Micro
SD card. Generally second path would be storage path of micro SD card(but not always). So you
need to check it out by executing the code with this method.
I created a new android project with empty activity, wrote the following code inside
File external_m3 =
https://riptutorial.com/ 1312
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
Output:
internal_m1: /data/data/your.application.package.appname/app_custom
internal_m2: /data/data/your.application.package.appname/files
external_m1: /storage/emulated/0
external_m2: /storage/emulated/0/Android/data/your.application.package.appname/files
external_m2_Args:
/storage/emulated/0/Android/data/your.application.package.appname/files/Pictures
external_m3: /storage/emulated/0/Pictures
Note: I have connected my phone to Windows PC; enabled both developer options, USB
debugging and then ran this code. If you do not connect your phone; but instead run this on
Android emulator, your output may vary. My phone model is Coolpad Note 3 - running on
Android 5.1
Note that /sdcard & /storage/emulated/0 also point to Internal Storage(UT). But these are symlinks
to /storage/sdcard0.
To clearly understand different storage paths in Android, Please go through this answer
Disclaimer: All the storage paths mentioned above are paths on my phone. Your files may not be
stored on same storage paths. Because, the storage locations/paths may vary on other mobile
phones depending on your vendor, manufacturer and different versions of Android OS.
https://riptutorial.com/ 1313
Save Database on SD Card (Backup DB on SD)
ExportDB("myDB.db","com.example.exam","/myFolder");
https://riptutorial.com/ 1314
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
https://riptutorial.com/ 1315
}
}
}
printDirectoryList();
}
}
Usage
To fetch inner files/folder of specific directory use same method just change argument,
pass the current selected path in argument and handle response for same.
return extension;
}
https://riptutorial.com/ 1316
Chapter 229: Strict Mode Policy : A tool to
catch the bug in the Compile Time.
Introduction
Strict Mode is a special class introduced in Android 2.3 for debugging. This developer tools detect
things done accidentally and bring them to our attention so that we can fix them. It is most
commonly used to catch the accidental disk or network access on the applications’ main thread,
where UI operations are received and animations takes place. StrictMode is basically a tool to
catch the bug in the Compile Time mode.
Remarks
StrictMode is basically a tool to catch the bug in the Compile Time mode. Using this we can avoid
the memory leaks in our applications.
Examples
The below Code Snippet is to setup the StrictMode for Thread Policies. This
Code is to be set at the entry points to our application.
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskWrites()
.penaltyLog() //Logs a message to LogCat
.build())
The below code deals with leaks of memory, like it detects when in SQLLite
finalize is called or not.
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectActivityLeaks()
.detectLeakedClosableObjects()
.penaltyLog()
.build());
Read Strict Mode Policy : A tool to catch the bug in the Compile Time. online:
https://riptutorial.com/android/topic/8756/strict-mode-policy---a-tool-to-catch-the-bug-in-the-
compile-time-
https://riptutorial.com/ 1317
Chapter 230: Supporting Screens With
Different Resolutions, Sizes
Remarks
Terms and concepts
Screen size
Actual physical size, measured as the screen's diagonal. For simplicity, Android groups
all actual screen sizes into four generalized sizes: small, normal, large, and extra-large.
Screen density
The quantity of pixels within a physical area of the screen; usually referred to as dpi
(dots per inch). For example, a "low" density screen has fewer pixels within a given
physical area, compared to a "normal" or "high" density screen. For simplicity, Android
groups all actual screen densities into six generalized densities: low, medium, high,
extra-high, extra-extra-high, and extra-extra-extra-high.
Orientation
The orientation of the screen from the user's point of view. This is either landscape or
portrait, meaning that the screen's aspect ratio is either wide or tall, respectively. Be
aware that not only do different devices operate in different orientations by default, but
the orientation can change at runtime when the user rotates the device. Resolution The
total number of physical pixels on a screen. When adding support for multiple screens,
applications do not work directly with resolution; applications should be concerned only
with screen size and density, as specified by the generalized size and density groups.
Density-independent pixel (dp) A virtual pixel unit that you should use when defining UI
layout, to express layout dimensions or position in a density-independent way. The
density-independent pixel is equivalent to one physical pixel on a 160 dpi screen,
which is the baseline density assumed by the system for a "medium" density screen. At
runtime, the system transparently handles any scaling of the dp units, as necessary,
based on the actual density of the screen in use. The conversion of dp units to screen
pixels is simple: px = dp * (dpi / 160). For example, on a 240 dpi screen, 1 dp equals
1.5 physical pixels. You should always use dp units when defining your application's
UI, to ensure proper display of your UI on screens with different densities.
Units
https://riptutorial.com/ 1318
• px
Pixels - corresponds to actual pixels on the screen.
• in
Inches - based on the physical size of the screen. 1 Inch = 2.54 centimeters
• mm
Millimeters - based on the physical size of the screen.
• pt
Points - 1/72 of an inch based on the physical size of the screen.
• dp or dip
Density-independent Pixels - an abstract unit that is based on the physical
density of the screen. These units are relative to a 160 dpi screen, so one dp is
one pixel on a 160 dpi screen. The ratio of dp-to-pixel will change with the screen
density, but not necessarily in direct proportion. Note: The compiler accepts both
"dip" and "dp", though "dp" is more consistent with "sp".
• sp
Scale-independent Pixels - this is like the dp unit, but it is also scaled by the
user's font size preference. It is recommend you use this unit when specifying
font sizes, so they will be adjusted for both the screen density and user's
preference. From Understanding Density Independence In Android:
px Pixels Varies No No
Density
dp ~160 Yes No
Independent Pixels
https://riptutorial.com/ 1319
Units Per Density Same Physical Size
Unit Description
Physical Inch Independent On Every Screen
Scale Independent
sp ~160 Yes No
Pixels
References:
• https://developer.android.com/guide/practices/screens_support.html
• http://developer.android.com/guide/topics/resources/more-resources.html
Examples
Using configuration qualifiers
Android supports several configuration qualifiers that allow you to control how the system selects
your alternative resources based on the characteristics of the current device screen. A
configuration qualifier is a string that you can append to a resource directory in your Android
project and specifies the configuration for which the resources inside are designed.
1. Create a new directory in your project's res/ directory and name it using the format:
<resources_name>-<qualifier>. <resources_name> is the standard resource name (such as
drawable or layout).
2. <qualifier> is a configuration qualifier, specifying the screen configuration for which these
resources are to be used (such as hdpi or xlarge).
For example, the following application resource directories provide different layout designs for
different screen sizes and different drawables. Use the mipmap/ folders for launcher icons.
When you need to set a pixel value for something like Paint.setTextSize but still want it be scaled
based on the device, you can convert dp and sp values.
https://riptutorial.com/ 1320
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
float pixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, metrics);
Alternatively, you can convert a dimension resource to pixels if you have a context to load the
resource from.
style="@android:style/TextAppearance.Small"
style="@android:style/TextAppearance.Medium"
style="@android:style/TextAppearance.Large"
<TextView
android:id="@+id/TextViewTopBarTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Small"/>
<TextView
android:id="@+id/TextViewTopBarTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Using this, you can avoid testing and specifying dimensions for different screen sizes.
https://riptutorial.com/ 1321
Read Supporting Screens With Different Resolutions, Sizes online:
https://riptutorial.com/android/topic/1086/supporting-screens-with-different-resolutions--sizes
https://riptutorial.com/ 1322
Chapter 231: Swipe to Refresh
Syntax
1. setColorSchemeResources sets the colors of the SwipeToRefreshLayout indicator
2. setOnRefreshListener sets what to do when layout is swiped
3. app:layout_behavior="@string/appbar_scrolling_view_behavior" if you have a Toolbar
with your layout, add this with the scrollFlags in Toolbar and the Toolbar will slide up while
scrolling down and slide in again while scrolling up.
Examples
Swipe To Refresh with RecyclerView
To add a Swipe To Refresh layout with a RecyclerView add the following to your
Activity/Fragment layout file:
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:scrollbars="vertical" />
</android.support.v4.widget.SwipeRefreshLayout>
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
// Execute code when refresh layout swiped
}
});
https://riptutorial.com/ 1323
Make sure the following dependency is added to your app's build.gradle file under dependencies:
compile 'com.android.support:support-core-ui:24.2.0'
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v4.widget.SwipeRefreshLayout>
https://riptutorial.com/ 1324
Chapter 232: SyncAdapter with periodically
do sync of data
Introduction
The sync adapter component in your app encapsulates the code for the tasks that transfer data
between the device and a server. Based on the scheduling and triggers you provide in your app,
the sync adapter framework runs the code in the sync adapter component.
Recently i worked on SyncAdapter i want share my knowledge with others,it may help others.
Examples
Sync adapter with every min requesting value from server.
<provider
android:name=".DummyContentProvider"
android:authorities="sample.map.com.ipsyncadapter"
android:exported="false" />
<!-- This service implements our SyncAdapter. It needs to be exported, so that the system
sync framework can access it. -->
<service android:name=".SyncService"
android:exported="true">
<!-- This intent filter is required. It allows the system to launch our sync service
as needed. -->
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<!-- This points to a required XML file which describes our SyncAdapter. -->
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>
<!-- This implements the account we'll use as an attachment point for our SyncAdapter.
Since
our SyncAdapter doesn't need to authenticate the current user (it just fetches a public
RSS
feed), this account's implementation is largely empty.
https://riptutorial.com/ 1325
This code need to be add in manifest file
In app we need to create the xml package to add syncadpter and authenticator xml files.
authenticator.xml
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="@string/R.String.accountType"
android:icon="@mipmap/ic_launcher"
android:smallIcon="@mipmap/ic_launcher"
android:label="@string/app_name"
/>
syncadapter
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="@string/R.String.contentAuthority"
android:accountType="@string/R.String.accountType"
android:userVisible="true"
android:allowParallelSyncs="true"
android:isAlwaysSyncable="true"
android:supportsUploading="false"/>
Authenticator
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.os.Bundle;
@Override
public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse,
String s) {
return null;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse, String
s, String s1, String[] strings, Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse
accountAuthenticatorResponse, Account account, Bundle bundle) throws NetworkErrorException {
return null;
}
https://riptutorial.com/ 1326
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse,
Account account, String s, Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public String getAuthTokenLabel(String s) {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
Account account, String s, Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse,
Account account, String[] strings) throws NetworkErrorException {
return null;
}
}
AuthenticatorService
public AuthenticatorService() {
super();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
IBinder ret = null;
if (intent.getAction().equals(AccountManager.ACTION_AUTHENTICATOR_INTENT)) ;
ret = getAuthenticator().getIBinder();
return ret;
}
IpDataDBHelper
https://riptutorial.com/ 1327
public static final String COLUMN_IP="ip";
public static final String COLUMN_COUNTRY_CODE="country_code";
public static final String COLUMN_COUNTRY_NAME="country_name";
public static final String COLUMN_CITY="city";
public static final String COLUMN_LATITUDE="latitude";
public static final String COLUMN_LONGITUDE="longitude";
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
String CREATE_TABLE="CREATE TABLE " + TABLE_IP_DATA + "( " + COLUMN_ID + " INTEGER
PRIMARY KEY ,"
+ COLUMN_IP + " INTEGER ," + COLUMN_COUNTRY_CODE + " INTEGER ," +
COLUMN_COUNTRY_NAME +
" TEXT ," + COLUMN_CITY + " TEXT ," + COLUMN_LATITUDE + " INTEGER ," +
COLUMN_LONGITUDE + " INTEGER)";
sqLiteDatabase.execSQL(CREATE_TABLE);
Log.d("SQL",CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TABLE_IP_DATA);
onCreate(sqLiteDatabase);
}
MainActivity
https://riptutorial.com/ 1328
private static final String ACCOUNT_TYPE="sample.map.com.ipsyncadapter";
private static final String AUTHORITY="sample.map.com.ipsyncadapter";
private static final String ACCOUNT_NAME="Sync";
mIp.setText(txtIp);
mCountryCod.setText(txtCC);
mCountryName.setText(txtCN);
mCity.setText(txtC);
mLatitude.setText(txtLP);
mLongitude.setText(txtLN);
mAccount=createSyncAccount(this);
//In this code i am using content provider to save data.
/* Cursor
cursor=getContentResolver().query(MyIPContentProvider.CONTENT_URI,null,null,null,null);
cursorAdapter=new SimpleCursorAdapter(this,R.layout.list_item,cursor,new String
[]{"ip","country_code","country_name","city","latitude","longitude"},
new int[]
{R.id.txt_ip,R.id.txt_country_code,R.id.txt_country_name,R.id.txt_city,R.id.txt_latitude,R.id.txt_longi
mListView.setAdapter(cursorAdapter);
getContentResolver().registerContentObserver(MyIPContentProvider.CONTENT_URI,true,new
StockContentObserver(new Handler()));
*/
Bundle settingBundle=new Bundle();
settingBundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL,true);
settingBundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED,true);
ContentResolver.requestSync(mAccount,AUTHORITY,settingBundle);
ContentResolver.setSyncAutomatically(mAccount,AUTHORITY,true);
ContentResolver.addPeriodicSync(mAccount,AUTHORITY,Bundle.EMPTY,60);
}
https://riptutorial.com/ 1329
private Account createSyncAccount(MainActivity mainActivity) {
Account account=new Account(ACCOUNT_NAME,ACCOUNT_TYPE);
AccountManager
accountManager=(AccountManager)mainActivity.getSystemService(ACCOUNT_SERVICE);
if(accountManager.addAccountExplicitly(account,null,null))
{
}else
{
}
return account;
}
cursorAdapter.swapCursor(getContentResolver().query(MyIPContentProvider.CONTENT_URI, null,
null, null, null));
}
}
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(syncStaredReceiver, new IntentFilter(SyncAdapter.SYNC_STARTED));
registerReceiver(syncFinishedReceiver, new
IntentFilter(SyncAdapter.SYNC_FINISHED));
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(syncStaredReceiver);
unregisterReceiver(syncFinishedReceiver);
}
private BroadcastReceiver syncFinishedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Sync finished!");
Toast.makeText(getApplicationContext(), "Sync Finished",
Toast.LENGTH_SHORT).show();
}
};
private BroadcastReceiver syncStaredReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Sync started!");
Toast.makeText(getApplicationContext(), "Sync started...",
Toast.LENGTH_SHORT).show();
}
};
https://riptutorial.com/ 1330
}
MyIPContentProvider
static
{
URI_MATCHER.addURI(AUTHORITY,TABLE_IP_DATA,IP_DATA);
}
@Override
public boolean onCreate() {
myDB=new IpDataDBHelper(getContext(),null,null,1);
return false;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
int uriType=URI_MATCHER.match(uri);
Cursor cursor=null;
switch (uriType)
{
case IP_DATA:
cursor=myDB.getAllIpData();
break;
default:
throw new IllegalArgumentException("UNKNOWN URL");
}
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
int uriType=URI_MATCHER.match(uri);
long id=0;
switch (uriType)
{
case IP_DATA:
id=myDB.AddIPData(contentValues);
break;
default:
throw new IllegalArgumentException("UNKNOWN URI :" +uri);
https://riptutorial.com/ 1331
}
getContext().getContentResolver().notifyChange(uri,null);
return Uri.parse(contentValues + "/" + id);
}
@Override
public int delete(Uri uri, String s, String[] strings) {
int uriType=URI_MATCHER.match(uri);
int rowsDeleted=0;
switch (uriType)
{
case IP_DATA:
rowsDeleted=myDB.deleteAllIpData();
break;
default:
throw new IllegalArgumentException("UNKNOWN URI :" +uri);
}
getContext().getContentResolver().notifyChange(uri,null);
return rowsDeleted;
}
@Override
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
return 0;
}
SyncAdapter
@Override
public void onPerformSync(Account account, Bundle bundle, String s, ContentProviderClient
contentProviderClient, SyncResult syncResult) {
Log.i(TAG, "onPerformSync");
https://riptutorial.com/ 1332
mContentResolver.delete(MyIPContentProvider.CONTENT_URI,null,null);
String data="";
try {
URL url =new URL("https://freegeoip.net/json/");
Log.d(TAG, "URL :"+url);
HttpURLConnection connection=(HttpURLConnection)url.openConnection();
Log.d(TAG,"Connection :"+connection);
connection.connect();
Log.d(TAG,"Connection 1:"+connection);
InputStream inputStream=connection.getInputStream();
data=getInputData(inputStream);
Log.d(TAG,"Data :"+data);
}
}catch(Exception e){
e.printStackTrace();
}
https://riptutorial.com/ 1333
/*Log.d(TAG,"Builder 2:"+ bufferedReader.readLine());
while ((data=bufferedReader.readLine())!= null);
{
builder.append(data);
Log.d(TAG,"Builder :"+data);
}
Log.d(TAG,"Builder 1 :"+data);
bufferedReader.close();*/
String data=bufferedReader.readLine();
bufferedReader.close();
return data.toString();
}
SyncService
@Override
public void onCreate() {
synchronized (syncAdapterLock)
{
if(syncAdapter==null)
{
syncAdapter =new SyncAdapter(getApplicationContext(),true);
}
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return syncAdapter.getSyncAdapterBinder();
}
https://riptutorial.com/ 1334
Chapter 233: TabLayout
Examples
Using a TabLayout without a ViewPager
Most of the time a TabLayout is used together with a ViewPager, in order to get the swipe
functionality that comes with it.
<android.support.design.widget.TabLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/tabLayout" />
For navigation within an Activity, manually populate the UI based on the tab selected.
@Override
public void onTabUnselected(TabLayout.Tab tab) {
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
https://riptutorial.com/ 1335
Chapter 234: TensorFlow
Introduction
TensorFlow was designed with mobile and embedded platforms in mind. We have sample code
and build support you can try now for these platforms:
Remarks
Appreciated work by MindRocks
Examples
How to use
Install Bazel from here. Bazel is the primary build system for TensorFlow. Now, edit the
WORKSPACE, we can find the WORKSPACE file in the root directory of the TensorFlow that we
have cloned earlier.
# Uncomment and update the paths in these entries to build the Android demo.
#android_sdk_repository(
# name = "androidsdk",
# api_level = 23,
# build_tools_version = "25.0.1",
# # Replace with path to Android SDK on your system
# path = "<PATH_TO_SDK>",
#)
#
#android_ndk_repository(
# name="androidndk",
# path="<PATH_TO_NDK>",
# api_level=14)
android_sdk_repository(
name = "androidsdk",
api_level = 23,
build_tools_version = "25.0.1",
# Replace with path to Android SDK on your system
path = "/Users/amitshekhar/Library/Android/sdk/",
)
android_ndk_repository(
name="androidndk",
path="/Users/amitshekhar/Downloads/android-ndk-r13/",
api_level=14)
https://riptutorial.com/ 1336
Read TensorFlow online: https://riptutorial.com/android/topic/9991/tensorflow
https://riptutorial.com/ 1337
Chapter 235: Testing UI with Espresso
Remarks
Espresso
Espresso cheat sheet will help you write your tests and what you want to test:
https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/
https://google.github.io/android-testing-support-library/docs/espresso/index.html
Troubleshooting
• When trying to scroll, be sure to close the keyboard first:
Watchout: not using the "Espresso" version won't do anything when used outside a ViewAction.
This may not be obvious if you have an import on the ViewAction version since they have exactly
the same method name.
ViewActions.closeSoftKeyboard;
Espresso.closeSoftKeyboard();
• When running tests together in a suite rather than individually, be aware that the Activity
from the previous test may still be running. Do not rely on the previous test's onDestroy()
being called before the current tests onResume(). It turns out this is actually a bug:
http://b.android.com/201513
Examples
Set Up Espresso
In the build.gradle file of your Android app module add next dependencies:
dependencies {
// Android JUnit Runner
androidTestCompile 'com.android.support.test:runner:0.5'
// JUnit4 Rules
androidTestCompile 'com.android.support.test:rules:0.5'
// Espresso core
https://riptutorial.com/ 1338
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks,
CountingIdlingResource
androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2.2'
//UI Automator tests
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.2.2'
}
Specify the AndroidJUnitRunner for the testInstrumentationRunner parameter in the build.gradle file.
android {
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
https://riptutorial.com/ 1339
return isAssignableFrom(DrawerLayout.class);
}
UI testing tools
Two main tools that are nowadays mostly used for UI testing are Appium and Espresso.
Appium Espresso
https://riptutorial.com/ 1340
Appium Espresso
dependencies {
// Set this dependency so you can use Android JUnit Runner
androidTestCompile 'com.android.support.test:runner:0.5'
// Set this dependency to use JUnit 4 rules
androidTestCompile 'com.android.support.test:rules:0.5'
// Set this dependency to build and run Espresso tests
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
// Set this dependency to build and run UI Automator tests
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.2.2'
}
NOTE If you are using latest support libraries, annotations etc. you need to exclude the older
versions from espresso to avoid collisions:
https://riptutorial.com/ 1341
androidTestCompile('com.android.support.test.espresso:espresso-web:2.2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
androidTestCompile('com.android.support.test:runner:0.5') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
androidTestCompile('com.android.support.test:rules:0.5') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
Other than these imports it is necessary to add android instrumentation test runner to build.gradle
android.defaultConfig:
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Device setup
For non flaky test it is recommended to set following settings on your devices:
Quite a setup from the real world ha? Well now when thats out of the way lets take a look how to
setup a small test
https://riptutorial.com/ 1342
The screen contains:
https://riptutorial.com/ 1343
/**
* Testing of the snackbar activity.
**/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class SnackbarActivityTest{
//espresso rule which tells which activity to start
@Rule
public final ActivityTestRule<SnackbarActivity> mActivityRule =
new ActivityTestRule<>(SnackbarActivity.class, true, false);
@Override
public void tearDown() throws Exception {
super.tearDown();
//just an example how tear down should cleanup after itself
mDatabase.clear();
mSharedPrefs.clear();
}
@Override
public void setUp() throws Exception {
super.setUp();
//setting up your application, for example if you need to have a user in shared
//preferences to stay logged in you can do that for all tests in your setup
User mUser = new User();
mUser.setToken("randomToken");
}
/**
*Test methods should always start with "testXYZ" and it is a good idea to
*name them after the intent what you want to test
**/
@Test
public void testSnackbarIsShown() {
//start our activity
mActivityRule.launchActivity(null);
//check is our text entry displayed and enter some text to it
String textToType="new snackbar text";
onView(withId(R.id.textEntry)).check(matches(isDisplayed()));
onView(withId(R.id.textEntry)).perform(typeText(textToType));
//click the button to show the snackbar
onView(withId(R.id.shownSnackbarBtn)).perform(click());
//assert that a view with snackbar_id with text which we typed and is displayed
onView(allOf(withId(android.support.design.R.id.snackbar_text),
withText(textToType))) .check(matches(isDisplayed()));
}
}
As you noticed there are 3-4 things that you might notice come often:
onView(withXYZ) <-- viewMatchers with them you are able to find elements on screen
perform(click()) <-- viewActions, you can execute actions on elements you previously found
https://riptutorial.com/ 1344
library/docs/espresso/cheatsheet/index.html
Thats it, now you can run the test either with right clicking on the class name / test and selecting
Run test or with command:
./gradlew connectedFLAVORNAMEAndroidTest
Up Navigation
@Test
public void testUpNavigation() {
intending(hasComponent(ParentActivity.class.getName())).respondWith(new
Instrumentation.ActivityResult(0, null));
onView(withContentDescription("Navigate up")).perform(click());
intended(hasComponent(ParentActivity.class.getName()));
}
Note that this is a workaround and will collide with other Views that have the same content
description.
ViewActions.click()
ViewActions.typeText()
ViewActions.clearText()
onView(...).perform(click());
onView(withId(R.id.button_simple)).perform(click());
You can execute more than one action with one perform call:
onView(...).perform(typeText("Hello"), click());
If the view you are working with is located inside a ScrollView (vertical or horizontal), consider
preceding actions that require the view to be displayed (like click() and typeText()) with scrollTo()
. This ensures that the view is displayed before proceeding to the other action:
onView(...).perform(scrollTo(), click());
With the ViewMatchers you can find view in the current view hierarchy.
https://riptutorial.com/ 1345
To find a view, use the onView() method with a view matcher which selects the correct view. The
onView() methods return an object of type ViewInteraction.
onView(withId(R.id.my_view))
onView(withText("Hello World"))
Espresso by default has many matchers that help you find views that you need to do some checks
or interactions with them.
https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/
• withId(R.id.ID_of_object_you_are_looking_for);
• withText("Some text you expect object to have");
• isDisplayed() <-- check is the view visible
• doesNotExist() <-- check that the view does not exist
All of these are very useful for everyday use, but if you have more complex views writing your
custom matchers can make the tests more readable and you can reuse them in different places.
There are 2 most common type of matchers you can extend: TypeSafeMatcher
BoundedMatcher
Implementing TypeSafeMatcher requires you to check the instanceOf the view you are asserting
against, if its the correct type you match some of its properties against a value you provided to a
matcher.
For example, type safe matcher that validates an image view has correct drawable:
@Override
protected boolean matchesSafely(View target) {
https://riptutorial.com/ 1346
//Type check we need to do in TypeSafeMatcher
if (!(target instanceof ImageView)) {
return false;
}
//We fetch the image view from the focused view
ImageView imageView = (ImageView) target;
if (expectedId < 0) {
return imageView.getDrawable() == null;
}
//We get the drawable from the resources that we are going to compare with image view
source
Resources resources = target.getContext().getResources();
Drawable expectedDrawable = resources.getDrawable(expectedId);
resourceName = resources.getResourceEntryName(expectedId);
if (expectedDrawable == null) {
return false;
}
//comparing the bitmaps should give results of the matcher if they are equal
Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Bitmap otherBitmap = ((BitmapDrawable) expectedDrawable).getBitmap();
return bitmap.sameAs(otherBitmap);
}
@Override
public void describeTo(Description description) {
description.appendText("with drawable from resource id: ");
description.appendValue(expectedId);
if (resourceName != null) {
description.appendText("[");
description.appendText(resourceName);
description.appendText("]");
}
}
}
onView(withDrawable(R.drawable.someDrawable)).check(matches(isDisplayed()));
Bounded matchers are similar you just dont have to do the type check but, since that is done
automagically for you:
/**
* Matches a {@link TextInputFormView}'s input hint with the given resource ID
*
* @param stringId
* @return
*/
public static Matcher<View> withTextInputHint(@StringRes final int stringId) {
return new BoundedMatcher<View, TextInputFormView>(TextInputFormView.class) {
private String mResourceName = null;
https://riptutorial.com/ 1347
@Override
public void describeTo(final Description description) {
//fill these out properly so your logging and error reporting is more clear
description.appendText("with TextInputFormView that has hint ");
description.appendValue(stringId);
if (null != mResourceName) {
description.appendText("[");
description.appendText(mResourceName);
description.appendText("]");
}
}
@Override
public boolean matchesSafely(final TextInputFormView view) {
if (null == mResourceName) {
try {
mResourceName = view.getResources().getResourceEntryName(stringId);
} catch (Resources.NotFoundException e) {
throw new IllegalStateException("could not find string with ID " +
stringId, e);
}
}
return view.getResources().getString(stringId).equals(view.getHint());
}
};
}
http://hamcrest.org/
https://developer.android.com/reference/android/support/test/espresso/matcher/ViewMatchers.html
Overall Espresso
Setup Espresso :
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestCompile 'com.android.support.test:runner:0.5'
ViewMatchers – A collection of objects that implement Matcher<? super View> interface. You can
pass one or more of these to the onView method to locate a view within the current view hierarchy.
https://riptutorial.com/ 1348
https://riptutorial.com/ 1349
https://riptutorial.com/android/topic/3485/testing-ui-with-espresso
https://riptutorial.com/ 1350
Chapter 236: Text to Speech(TTS)
Examples
Text to Speech Base
layout_text_to_speech.xml
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter text here!"
android:id="@+id/textToSpeak"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/textToSpeak"
android:id="@+id/btnSpeak"/>
</RelativeLayout>
AndroidTextToSpeechActivity.java
@Override
public void onDestroy() {
// Don't forget to shutdown tts!
https://riptutorial.com/ 1351
if (tts != null) {
tts.stop();
tts.shutdown();
}
super.onDestroy();
}
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
int result = tts.setLanguage(Locale.US);
if (result == TextToSpeech.LANG_MISSING_DATA
|| result == TextToSpeech.LANG_NOT_SUPPORTED) {
Log.e("TTS", "This Language is not supported");
} else {
btnSpeak.setEnabled(true);
speakOut();
}
} else {
Log.e("TTS", "Initilization Failed!");
}
}
The language to be spoken can be set by providing a Locale to the setLanguage() method:
The number of supported languages varies between Android levels. The method
isLanguageAvailable() can be used to check if a certain language is supported:
tts.isLanguageAvailable(Locale.CHINESE);
The speech pitch level can be set by using the setPitch() method. By default, the pitch value is
1.0. Use values less than 1.0 to decrease the pitch level or values greater than 1.0 to increase the
pitch level:
tts.setPitch(0.6);
The speech rate can be set using setSpeechRate(). The default speech rate is 1.0. The speech rate
can be doubled by setting it to 2.0 or made half by setting it to 0.5:
https://riptutorial.com/ 1352
tts.setSpeechRate(2.0);
Cold observable implementation, emits true when TTS engine finishes speaking, starts speaking
when subscribed. Notice that API level 21 introduces different way to perform speaking:
WeakReference<Context> contextRef;
https://riptutorial.com/ 1353
}
}
if (languageCode == android.speech.tts.TextToSpeech.LANG_COUNTRY_AVAILABLE) {
textToSpeech.setPitch(1);
textToSpeech.setSpeechRate(1.0f);
textToSpeech.setOnUtteranceProgressListener(this);
performSpeak();
} else {
emitter.onError(new Throwable("language " + selectedLocale.getCountry() + " is not
supported"));
}
}
void performSpeak() {
if (isAtLeastApiLevel(21)) {
speakWithNewApi();
} else {
speakWithOldApi();
}
}
https://riptutorial.com/ 1354
textToSpeech.speak(text, TextToSpeech.QUEUE_ADD, params, uniqueId());
}
void speakWithOldApi() {
HashMap<String, String> map = new HashMap<>();
map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, uniqueId());
textToSpeech.speak(text, TextToSpeech.QUEUE_ADD, map);
}
https://riptutorial.com/ 1355
Chapter 237: TextInputLayout
Introduction
TextInputLayout was introduced to display the floating label on EditText. The EditText has to be
wrapped by TextInputLayout in order to display the floating label.
Remarks
TextInputLayout is a layout which wraps an EditText (or descendant) to show a floating label when
the hint is hidden due to the user inputting text. Additonally the TextInputLayout enables you to
display an error message below the EditText.
Make sure the following dependency is added to your app's build.gradle file under dependencies:
compile 'com.android.support:design:25.3.1'
Examples
Basic usage
Example:
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/username"/>
</android.support.design.widget.TextInputLayout>
Handling Errors
You can use the TextInputLayout to display error messages according to the material design
guidelines using the setError and setErrorEnabledmethods.
https://riptutorial.com/ 1356
To enable error in the TextInputLayout you can eithr use app:errorEnabled="true" in xml or
til.setErrorEnabled(true); as shown above.
The TextInputLayout has a character counter for an EditText defined within it.
The counter will be rendered below the EditText.
<android.support.design.widget.TextInputLayout
app:counterEnabled="true"
app:counterMaxLength="15">
<EditText/>
</android.support.design.widget.TextInputLayout>
With an input password type, you can also enable an icon that can show or hide the entire text
using the passwordToggleEnabled attribute.
Example:
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:passwordToggleContentDescription="@string/description"
app:passwordToggleDrawable="@drawable/another_toggle_drawable"
https://riptutorial.com/ 1357
app:passwordToggleEnabled="true">
<EditText/>
</android.support.design.widget.TextInputLayout>
TextInputEditText
The TextInputEditText is an EditText with an extra fix to display a hint in the IME when in 'extract'
mode.
The Extract mode is the mode that the keyboard editor switches to when you click on an EditText
when the space is too small (for example landscape on a smartphone).
In this case, using an EditText while you are editing the text you can see that the IME doesn't give
you a hint of what you're editing
The TextInputEditText fixes this issue providing hint text while the user’s device’s IME is in Extract
mode.
Example:
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Description"
>
<android.support.design.widget.TextInputEditText
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.design.widget.TextInputLayout>
You can customize the appearance of the TextInputLayout and its embedded EditTextby defining
custom styles in your styles.xml. The defined styles can either be added as styles or themes to
your TextInputLayout.
styles.xml:
https://riptutorial.com/ 1358
To Apply Style update your TextInputLayout And EditText as follows
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:hintTextAppearance="@style/MyHintStyle">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Title"
android:theme="@style/MyEditText" />
</android.support.design.widget.TextInputLayout>
Example to customize the accent color of the TextInputLayout. The accent color affects the color of
the baseline of the EditText and the text color for the floating hint text:
styles.xml:
layout file:
<android.support.design.widget.TextInputLayout
android:id="@+id/textInputLayout_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/TextInputLayoutWithPrimaryColor">
<android.support.design.widget.TextInputEditText
android:id="@+id/textInputEditText_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_hint_password"
android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>
https://riptutorial.com/ 1359
Chapter 238: TextView
Introduction
Everything related to TextView customization in Android SDK
Syntax
• TextView (Context context)
• (TextView)findViewById(int id)
• void setText (int resid)
• void setText (CharSequence text)//You can use String as an argument
Remarks
Try to use it in xml design or programmatically.
Examples
Textview with different Textsize
TextView customization
https://riptutorial.com/ 1360
public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.CustomTextView);
if (a.hasValue(R.styleable.CustomTextView_strokeColor)) {
float strokeWidth =
a.getDimensionPixelSize(R.styleable.CustomTextView_strokeWidth, 1);
int strokeColor = a.getColor(R.styleable.CustomTextView_strokeColor,
0xff000000);
float strokeMiter =
a.getDimensionPixelSize(R.styleable.CustomTextView_strokeMiter, 10);
Paint.Join strokeJoin = null;
switch (a.getInt(R.styleable.CustomTextView_strokeJoinStyle, 0)) {
case (0):
strokeJoin = Paint.Join.MITER;
break;
case (1):
strokeJoin = Paint.Join.BEVEL;
break;
case (2):
strokeJoin = Paint.Join.ROUND;
break;
}
this.setStroke(strokeWidth, strokeColor, strokeJoin, strokeMiter);
}
}
}
public void setStroke(float width, int color, Paint.Join join, float miter) {
strokeWidth = width;
strokeColor = color;
strokeJoin = join;
strokeMiter = miter;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
https://riptutorial.com/ 1361
Usage:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@mipmap/background">
<pk.sohail.gallerytest.activity.CustomTextView
android:id="@+id/pager_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:gravity="center"
android:text="@string/txt_title_photo_gallery"
android:textColor="@color/white"
android:textSize="30dp"
android:textStyle="bold"
app:outerShadowRadius="10dp"
app:strokeColor="@color/title_text_color"
app:strokeJoinStyle="miter"
app:strokeWidth="2dp" />
</RelativeLayout>
attars:
<declare-styleable name="CustomTextView">
https://riptutorial.com/ 1362
</resources>
Programmatically usage:
Spannable TextView
A spannable TextView can be used in Android to highlight a particular portion of text with a different
color, style, size, and/or click event in a single TextView widget.
TextView textview=findViewById(R.id.textview);
• Spannable font: In order to set a different font size to some portion of text, a
RelativeSizeSpan can be used, as shown in the following example:
https://riptutorial.com/ 1363
• Spannable typeface: In order to set a different font typeface to some portion of text, a
custom TypefaceSpan can be used, as shown in the following example:
However, in order to make the above code working, the class CustomTypefaceSpan has to be
derived from the class TypefaceSpan. This can be done as follows:
@Override
public void updateDrawState(TextPaint ds) {
applyCustomTypeFace(ds, newType);
}
@Override
public void updateMeasureState(TextPaint paint) {
applyCustomTypeFace(paint, newType);
}
paint.setTypeface(tf);
}
}
https://riptutorial.com/ 1364
TextView with image
Android allows programmers to place images at all four corners of a TextView. For example, if you
are creating a field with a TextView and at same time you want to show that the field is editable,
then developers will usually place an edit icon near that field. Android provides us an interesting
option called compound drawable for a TextView:
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:drawablePadding="4dp"
android:drawableRight="@drawable/edit"
android:text="Hello world"
android:textSize="18dp" />
You can set the drawable to any side of your TextView as follows:
android:drawableLeft="@drawable/edit"
android:drawableRight="@drawable/edit"
android:drawableTop="@drawable/edit"
android:drawableBottom="@drawable/edit"
Setting the drawable can also be achieved programmatically in the following way:
Setting any of the parameters handed over to setCompoundDrawables() to null will remove the icon
from the corresponding side of the TextView.
Strikethrough TextView
https://riptutorial.com/ 1365
spanBuilder.setSpan(
strikethroughSpan, // Span to add
0, // Start
4, // End of the span (exclusive)
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE // Text changes will not reflect in the strike
changing
);
textView.setText(spanBuilder);
MainActivity.java:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
activity_main.xml:
<com.customthemeattributedemo.customview.CustomTextView
style="?mediumTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:text="@string/message_hello"
custom:font_family="@string/bold_font" />
<com.customthemeattributedemo.customview.CustomTextView
style="?largeTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:text="@string/message_hello"
custom:font_family="@string/bold_font" />
</LinearLayout>
CustomTextView.java:
https://riptutorial.com/ 1366
private static final String TAG = "TextViewPlus";
private Context mContext;
attrs.xml:
<declare-styleable name="CustomTextView">
</declare-styleable>
</resources>
strings.xml:
<resources>
<string name="app_name">Custom Style Theme Attribute Demo</string>
<string name="message_hello">Hello Hiren!</string>
<string name="bold_font">bold.ttf</string>
</resources>
https://riptutorial.com/ 1367
styles.xml:
<resources>
<item name="mediumTextStyle">@style/textMedium</item>
<item name="largeTextStyle">@style/textLarge</item>
</style>
<style name="textParentStyle">
<item name="android:textColor">@android:color/white</item>
<item name="android:background">@color/colorPrimary</item>
<item name="android:padding">5dp</item>
</style>
</resources>
In order to make a RelativeSizeSpan align to the top, a custom class can be derived from the class
SuperscriptSpan. In the following example, the derived class is named TopAlignSuperscriptSpan:
activity_main.xml:
<TextView
android:id="@+id/txtView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:textSize="26sp" />
MainActivity.java:
TopAlignSuperscriptSpan.java:
https://riptutorial.com/ 1368
private class TopAlignSuperscriptSpan extends SuperscriptSpan {
//divide superscript by this number
protected int fontScale = 2;
//doesn't shift
TopAlignSuperscriptSpan() {}
@Override
public void updateDrawState( TextPaint tp ) {
//original ascent
float ascent = tp.ascent();
//move baseline to top of old font, then move down size of new font
//adjust for errors with shift percentage
tp.baselineShift += ( ascent - ascent * shiftPercentage )
- (newAscent - newAscent * shiftPercentage );
}
@Override
public void updateMeasureState( TextPaint tp ) {
updateDrawState( tp );
}
}
Reference screenshot:
Pinchzoom on TextView
https://riptutorial.com/ 1369
activity_main.xml:
<TextView
android:id="@+id/mytv"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="This is my sample text for pinch zoom demo, you can zoom in and out
using pinch zoom, thanks" />
</RelativeLayout>
MainActivity.java:
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.TextView;
https://riptutorial.com/ 1370
return true;
}
Colored text can be created by passing the text and a font color name to the following function:
The colored text can then be set to a TextView (or even to a Button, EditText, etc.) by using the
example code below.
txtView.setText(Html.fromHtml(name+" "+surName));
Reference screenshot:
https://riptutorial.com/ 1371
Read TextView online: https://riptutorial.com/android/topic/4212/textview
https://riptutorial.com/ 1372
Chapter 239: The Manifest File
Introduction
The Manifest is an obligatory file named exactly "AndroidManifest.xml" and located in the app's
root directory. It specifies the app name, icon, Java package name, version, declaration of
Activities, Services, app permissions and other information.
Examples
Declaring Components
The primary task of the manifest is to inform the system about the app's components. For
example, a manifest file can declare an activity as follows:
In the <application> element, the android:icon attribute points to resources for an icon that
identifies the app.
In the element, the android:name attribute specifies the fully qualified class name of the Activity
subclass and the android:label attribute specifies a string to use as the user-visible label for the
activity.
Activities, services, and content providers that you include in your source but do not declare in the
manifest are not visible to the system and, consequently, can never run. However, broadcast
receivers can be either declared in the manifest or created dynamically in code (as
BroadcastReceiver objects) and registered with the system by calling registerReceiver().
For more about how to structure the manifest file for your app, see The AndroidManifest.xml File
https://riptutorial.com/ 1373
documentation.
Any permission required by your application to access a protected part of the API or to interact
with other applications must be declared in your AndroidManifest.xml file. This is done using the
<uses-permission /> tag.
Syntax
<uses-permission android:name="string"
android:maxSdkVersion="integer"/>
android:maxSdkVersion: The highest API level at which this permission should be granted to
your app. Setting this permission is optional and should only be set if the permission your app
requires is no longer needed at a certain API level.
Sample AndroidManifest.xml:
<application>....</application>
</manifest>
https://riptutorial.com/ 1374
Chapter 240: Theme, Style, Attribute
Examples
Use Custom Theme Globally
In themes.xml:
In AndroidManifest.xml:
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
</application>
5.0
2.1.x
https://riptutorial.com/ 1375
In themes.xml:
In AndroidManifest.xml:
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat">
<activity
android:name=".MyActivity"
android:theme="@style/MyActivityTheme" />
</application>
5.0
You can use the same ripple color used by your app assigning the ?android:colorControlHighlight
in your views. You can customize this color by changing the android:colorControlHighlight
attribute in your theme:
This attribute can change the background of the Status Bar icons (at the top of the screen) to
https://riptutorial.com/ 1376
white.
The navigation bar (at the bottom of the screen) can be transparent. Here is the way to achieve it.
The Status Bar (top of the screen) can be made transparent, by applying this attribute to the style:
5.0
This attribute is used to change the navigation bar (one, that contain Back, Home Recent button).
Usually it is black, however it's color can be changed.
Theme inheritance
When defining themes, one usually uses the theme provided by the system, and then changes
modifies the look to fit his own application. For example, this is how the Theme.AppCompat theme is
inherited:
This theme now has all the properties of the standard Theme.AppCompat theme, except the ones we
explicitly changed.
There is also a shortcut when inheriting, usually used when one inherits from his own theme:
<style name="AppTheme.Red">
https://riptutorial.com/ 1377
<item name="colorAccent">@color/red</item>
</style>
Since it already has AppTheme. in the start of it's name, it automatically inherits it, without needing to
define the parent theme. This is useful when you need to create specific styles for a part (for
example, a single Activity) of your app.
Using more than one theme in your Android application, you can add custom colors to every
theme, to be like this:
</style>
<!-- -->
<style name="TwoTheme" parent="Theme.AppCompat.Light.DarkActionBar" >
</style>
......
<application
android:theme="@style/OneTheme"
...>
https://riptutorial.com/ 1378
Create new xml file named attrs.xml and add this code :
Go back to style.xml and add these colors with its values for each theme :
Now you have custom colors for each theme, let's add these color to our views.
<TextView>
android:id="@+id/txte_view"
android:textColor="?attr/custom_blue" />
Mow we can change the theme just by single line setTheme(R.style.TwoTheme); this line must be
before setContentView() method in onCreate() method, like this Activity.java :
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.TwoTheme);
setContentView(R.layout.main_activity);
....
}
https://riptutorial.com/ 1379
public class MyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (new MySettings(this).isDarkTheme())
setTheme(R.style.TwoTheme);
}
}
Finally, go to all your activities add make all of them extend the MyActivity base class:
In order to change the theme, just go to MyActivity and change R.style.TwoTheme to your theme (
R.style.OneTheme , R.style.ThreeTheme ....).
https://riptutorial.com/ 1380
Chapter 241: Thread
Examples
Thread Example with its description
While launching an application firstly main thread is executed. This Main thread handles all the UI
concept of application. If we want to run long the task in which we don't need the UI then we use
thread for running that task in background.
We can create thread by creating the object of Thread which have Thread.run() method for running
the thread.Here, run() method is called by the start() method.
We can also run the the multiple threads independently, which is known as MultiThreading. This
thread also have the functionality of sleep by which the currently executing thread to sleep
(temporarily cease execution) for the specified number of time. But sleep throws the
InterruptedException So, we have to handle it by using try/catch like this.
try{Thread.sleep(500);}catch(InterruptedException e){System.out.println(e);}
It is common to use a background Thread for doing network operations or long running tasks, and
then update the UI with the results when needed.
This poses a problem, as only the main thread can update the UI.
The solution is to use the runOnUiThread() method, as it allows you to initiate code execution on the
UI thread from a background Thread.
In this simple example, a Thread is started when the Activity is created, runs until the magic
number of 42 is randomly generated, and then uses the runOnUiThread() method to update the UI
once this condition is met.
TextView mTextView;
https://riptutorial.com/ 1381
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.my_text_view);
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText("Ready Player One");
}
});
}
}).start();
}
}
https://riptutorial.com/ 1382
Chapter 242: Time Utils
Examples
Convert Date Format into Milliseconds
To Convert you date in dd/MM/yyyy format into milliseconds you call this function with data as
String
return date;
}
This Method will convert given specific day,month and year into milliseconds. It will be very help
when using Timpicker or Datepicker
https://riptutorial.com/ 1383
It will return current date
This example will help to verify the given time is within a period or not.
GetCurrentRealTime
This calculate current device time and add/subtract difference between real and device time
https://riptutorial.com/ 1384
long bootTime = networkTime - SystemClock.elapsedRealtime();
Calendar calInstance = Calendar.getInstance();
calInstance.setTimeZone(getUTCTimeZone());
long currentDeviceTime = bootTime + SystemClock.elapsedRealtime();
calInstance.setTimeInMillis(currentDeviceTime);
return calInstance;
}
https://riptutorial.com/ 1385
Chapter 243: Toast
Introduction
A Toast provides simple feedback about an operation in a small popup and automatically
disappears after a timeout. It only fills the amount of space required for the message and the
current activity remains visible and interactive.
Syntax
• Toast makeText (Context context, CharSequence text, int duration)
• Toast makeText (Context context, int resId, int duration)
• void setGravity(int gravity, int xOffset, int yOffset)
• void show()
Parameters
Parameter Details
The context to display your Toast in. this is commonly used in an Activity and
context
getActivity() is commonly used in a Fragment
A CharSequence that specifies what text will be shown in the Toast. Any object
text
that implements CharSequence can be used, including a String
Integer flag representing how long the Toast will show. Options are
duration
Toast.LENGTH_SHORT and Toast.LENGTH_LONG
gravity Integer specifying the position, or "gravity" of the Toast. See options here
Remarks
A toast provides simple feedback about an operation in a small popup. It only fills the amount of
space required for the message and the current activity remains visible and interactive.
A more recent alternative to Toast is SnackBar. SnackBar offers an updated visual style and
allows the user to dismiss the message or take further action. See the SnackBar documentation
https://riptutorial.com/ 1386
for details.
Official Documentation:
https://developer.android.com/reference/android/widget/Toast.html
Examples
Set position of a Toast
A standard toast notification appears at the bottom of the screen aligned in horizontal centre. You
can change this position with the setGravity(int, int, int). This accepts three parameters: a
Gravity constant, an x-position offset, and a y-position offset.
For example, if you decide that the toast should appear in the top-left corner, you can set the
gravity like this:
toast.setGravity(Gravity.TOP|Gravity.LEFT, 0, 0);
In Android, a Toast is a simple UI element that can be used to give contextual feedback to a user.
Or, to show a Toast inline, without holding on to the Toast object you can:
IMPORTANT: Make sure that the show() method is called from the UI thread. If you're trying to
show a Toast from a different thread you can e.g. use runOnUiThread method of an Activity.
Failing to do so, meaning trying to modify the UI by creating a Toast, will throw a RuntimeException
which will look like this:
java.lang.RuntimeException: Can't create handler inside thread that has not called
https://riptutorial.com/ 1387
Looper.prepare()
The simplest way of handling this exception is just by using runOnUiThread: syntax is shown
below.
runOnUiThread(new Runnable() {
@Override
public void run() {
// Your code here
}
});
If you don't want to use the default Toast view, you can provide your own using the setView(View)
method on a Toast object.
First, create the XML layout you would like to use in your Toast.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toast_layout_root"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
android:background="#111">
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFF"/>
<TextView android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFF"/>
</LinearLayout>
Then, when creating your Toast, inflate your custom View from XML, and call setView
// Set the title and description TextViews from our custom layout
TextView title = (TextView) layout.findViewById(R.id.title);
title.setText("Toast Title");
https://riptutorial.com/ 1388
Toast toast = new Toast(getApplicationContext());
toast.setGravity(Gravity.CENTER, 0, 0);
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();
/**
* Thread safe way of displaying toast.
* @param message
* @param duration
*/
public void showToast(final String message, final int duration) {
getMainThreadHandler().post(new Runnable() {
@Override
public void run() {
if (!TextUtils.isEmpty(message)) {
if (toast != null) {
toast.cancel(); //dismiss current toast if visible
toast.setText(message);
} else {
toast = Toast.makeText(App.this, message, duration);
}
toast.show();
}
}
});
}
https://riptutorial.com/ 1389
Show Toast Message Above Soft Keyboard
By default, Android will display Toast messages at the bottom of the screen even if the keyboard is
showing. This will show a Toast message just above the keyboard.
If you don't want to extend Application and keep your toast messages thread safe, make sure you
show them in the post execute section of your AsyncTasks.
@Override
protected Void doInBackground(Void... params) {
// Do your background work here
}
@Override
protected void onPostExecute(Void aVoid) {
// Show toast messages here
Toast.makeText(context, "Ding! Your Toast is ready.", Toast.LENGTH_SHORT).show();
}
https://riptutorial.com/ 1390
Chapter 244: Tools Attributes
Remarks
Android has a dedicated XML namespace intended for tools to be able to record information in
XML file.
Examples
Designtime Layout Attributes
These attributes are used when the layout is rendered in Android Studio, but have no impact on
the runtime.
In general you can use any Android framework attribute, just using the tools: namespace rather
than the android: namespace for layout preview. You can add both the android: namespace
attribute (which is used at runtime) and the matching tools: attribute (which overrides the runtime
attribute in the layout preview only).
<EditText
tools:text="My Text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/ll1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:visibility="gone" />
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity" >
Or the showIn attribute to see and included layout preview in another layout
https://riptutorial.com/ 1391
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text"
tools:showIn="@layout/activity_main" />
https://riptutorial.com/ 1392
Chapter 245: Touch Events
Examples
How to vary between child and parent view group touch events
1. The onTouchEvents() for nested view groups can be managed by the boolean
onInterceptTouchEvent.
The parent's onTouchEvent is received before the child's. If the OnInterceptTouchEvent returns false, it
sends the motion event down the chain to the child's OnTouchEvent handler. If it returns true the
parent's will handle the touch event.
However there may be instances when we want some child elements to manage OnTouchEvents
and some to be managed by the parent view (or possibly the parent of the parent).
2. One way a child element can be protected from the parent's OnInterceptTouchEvent is by
implementing the requestDisallowInterceptTouchEvent.
This prevents any of the parent views from managing the OnTouchEvent for this element, if the
element has event handlers enabled.
3.
If the OnInterceptTouchEvent is false, the child element's OnTouchEvent will be evaluated. If you have
a methods within the child elements handling the various touch events, any related event handlers
that are disabled will return the OnTouchEvent to the parent.
This answer:
A visualisation of how the propagation of touch events passes through:
parent -> child|parent -> child|parent -> child views.
https://riptutorial.com/ 1393
Courtesy from here
4. Another way is returning varying values from the OnInterceptTouchEvent for the parent.
This example taken from Managing Touch Events in a ViewGroup and demonstrates how to
intercept the child's OnTouchEvent when the user is scrolling.
4a.
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
* This method JUST determines whether we want to intercept the motion.
* If we return true, onTouchEvent will be called and we do the actual
* scrolling there.
*/
https://riptutorial.com/ 1394
mIsScrolling = false;
return false; // Do not intercept touch event, let the child handle it
}
switch (action) {
case MotionEvent.ACTION_MOVE: {
if (mIsScrolling) {
// We're currently scrolling, so yes, intercept the
// touch event!
return true;
}
This is some code from the same link showing how to create the parameters of the rectangle
around your element:
4b.
// Instantiate a TouchDelegate.
// "delegateArea" is the bounds in local coordinates of
// the containing view to be mapped to the delegate view.
// "myButton" is the child view that should receive motion
// events.
TouchDelegate touchDelegate = new TouchDelegate(delegateArea, myButton);
https://riptutorial.com/ 1395
}
https://riptutorial.com/ 1396
Chapter 246: TransitionDrawable
Examples
Add transition or Cross-fade between two images.
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/image1"/>
<item android:drawable="@drawable/image2"/>
</transition>
The image1 and image2 are the two images that we want to transition and they should be put in
your res/drawable folder too.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<ImageView
android:id="@+id/image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/image1"/>
</LinearLayout>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
https://riptutorial.com/ 1397
transitionDrawable = (TransitionDrawable)
ContextCompat.getDrawable(this, R.drawable.transition);
birdImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View view) {
birdImageView.setImageDrawable(transitionDrawable);
transitionDrawable.startTransition(1000);
}
});
}
https://riptutorial.com/ 1398
Chapter 247: Twitter APIs
Examples
Creating login with twitter button and attach a callback to it
1. Inside your layout, add a Login button with the following code:
<com.twitter.sdk.android.core.identity.TwitterLoginButton
android:id="@+id/twitter_login_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
2. In the Activity or Fragment that displays the button, you need to create and attach a Callback
to the Login Buttonas the following:
import com.twitter.sdk.android.core.Callback;
import com.twitter.sdk.android.core.Result;
import com.twitter.sdk.android.core.TwitterException;
import com.twitter.sdk.android.core.TwitterSession;
import com.twitter.sdk.android.core.identity.TwitterLoginButton;
...
@Override
public void failure(TwitterException exception) {
// Do something on failure
}
});
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Make sure that the loginButton hears the result from any
// Activity that it triggered.
loginButton.onActivityResult(requestCode, resultCode, data);
}
Note, If using the TwitterLoginButton in a Fragment, use the following steps instead:
https://riptutorial.com/ 1399
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Pass the activity result to the fragment, which will then pass the result to the
login
// button.
Fragment fragment = getFragmentManager().findFragmentById(R.id.your_fragment_id);
if (fragment != null) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
repositories {
maven { url 'https://maven.fabric.io/public' }
}
compile('com.twitter.sdk.android:twitter:1.14.1@aar') {
transitive = true;
}
https://riptutorial.com/ 1400
Chapter 248: Typedef Annotations: @IntDef,
@StringDef
Remarks
The annotations package includes a number of useful metadata annotations you can decorate
your own code with, to help catch bugs.
dependencies {
compile 'com.android.support:support-annotations:25.3.1'
}
Examples
IntDef Annotations
This annotation ensures that only the valid integer constants that you expect are used.
The following example illustrates the steps to create an annotation:
import android.support.annotation.IntDef;
//Tell the compiler not to store annotation data in the .class file
@Retention(RetentionPolicy.SOURCE)
//Declare the CarType annotation
public @interface CarType {}
@CarType
private int mType;
@CarType
public int getCarType(){
return mType;
};
https://riptutorial.com/ 1401
}
They also enable code completion to automatically offer the allowed constants.
When you build this code, a warning is generated if the type parameter does not reference one of
the defined constants.
Using the IntDef#flag() attribute set to true, multiple constants can be combined.
//Tell the compiler not to store annotation data in the .class file
@Retention(RetentionPolicy.SOURCE)
.....
Users can combine the allowed constants with a flag (such as |, &, ^ ).
https://riptutorial.com/ 1402
Chapter 249: UI Lifecycle
Examples
Saving data on memory trimming
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_example);
if(savedInstanceState != null) {
mArg = savedInstanceState.getInt(EXAMPLE_ARG);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(EXAMPLE_ARG, mArg);
}
}
Explanation
The Android system will always strive to clear as much memory as it can. So, if your activity is
down to the background, and another foreground activity is demanding its share, the Android
system will call onTrimMemory() on your activity.
But that doesn't mean that all your properties should vanish. What you should do is to save them
into a Bundle object. Bundle object are much better handled memory wise. Inside a bundle every
object is identified by unique text sequence - in the example above integer value variable mArg is
hold under reference name EXAMPLE_ARG. And when the activity is recreated extract your old values
from the Bundle object instead of recreating them from scratch
https://riptutorial.com/ 1403
Chapter 250: Unit testing in Android with
JUnit
Remarks
• Vogella: Unit Testing with JUnit
• Junit Annotations: java2novice.com
• Assert Class: junit.org
• JUnit Api: tutorialspoint.com
• Anroid testing Medium.com posts
Examples
Creating Local unit tests
Breakdown
The test class, you can create several test classes and place them inside the test package.
@Test
public void addition_isCorrect() {
...
}
The test method, several test methods can be created inside a test class.
https://riptutorial.com/ 1404
Notice the annotation @Test.
The Test annotation tells JUnit that the public void method to which it is attached can
be run as a test case.
There are several other useful annotations like @Before, @After etc. This page would be a good
place to start.
These methods are member of the Assert class. Some other useful methods are assertFalse(),
assertNotNull(), assertTrue etc. Here's an elaborate Explanation.
@Test: The Test annotation tells JUnit that the public void method to which it is attached can be
run as a test case. To run the method, JUnit first constructs a fresh instance of the class then
invokes the annotated method.
@Before: When writing tests, it is common to find that several tests need similar objects created
before they can run. Annotating a public void method with @Before causes that method to be run
before the Test method.
@After: If you allocate external resources in a Before method you need to release them after the
test runs. Annotating a public void method with @After causes that method to be run after the Test
method. All @After methods are guaranteed to run even if a Before or Test method throws an
exception
https://riptutorial.com/ 1405
A lot of the value from local JVM unit tests comes from the way you design your application. You
have to design it in such a way where you can decouple your business logic from your Android
Components. Here is an example of such a way using the Model-View-Presenter pattern. Lets
practice this out by implementing a basic sign up screen that only takes a username and
password. Our Android app is responsible for validating that the username the user supplies is not
blank and that the password is at least eight characters long and contains at least one digit. If the
username/password is valid we perform our sign up api call, otherwise we display an error
message.
Here we define in a single class, LoginContract, that will house the various interactions between
our various classes.
Our LoginActivity is for the most part the same except that we have removed the responsibility of
having to know how to validate a user's sign up form (our business logic). The LoginActivity will
now rely on our new LoginPresenter to perform validation.
https://riptutorial.com/ 1406
...
Now your business logic will reside in your new LoginPresenter class.
And now we can create local JVM unit tests against your new LoginPresenter class.
@Mock
LoginContract.View view;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
presenter = new LoginPresenter(view);
}
@Test
public void test_validateUserCredentials_userDidNotEnterUsername_displayErrorMessage()
throws Exception {
String username = "";
String password = "kingslayer1";
presenter.validateUserCredentials(username, password);
Mockito.verify(view). displayInvalidCredentialsErrorMessage();
}
@Test
public void
test_validateUserCredentials_userEnteredFourLettersAndOneDigitPassword_displayErrorMessage()
throws Exception {
https://riptutorial.com/ 1407
String username = "Jaime Lanninster";
String password = "king1";
presenter.validateUserCredentials(username, password);
Mockito.verify(view). displayInvalidCredentialsErrorMessage();
}
@Test
public void
test_validateUserCredentials_userEnteredNineLettersButNoDigitsPassword_displayErrorMessage()
throws Exception {
String username = "Jaime Lanninster";
String password = "kingslayer";
presenter.validateUserCredentials(username, password);
Mockito.verify(view). displayInvalidCredentialsErrorMessage();
}
@Test
public void
test_validateUserCredentials_userEnteredNineLettersButOneDigitPassword_performApiCallToSignUpUser()
throws Exception {
String username = "Jaime Lanninster";
String password = "kingslayer1";
presenter.validateUserCredentials(username, password);
Mockito.verify(view).performSignUpApiCall(username, password);
}
}
As you can see, when we extracted our business logic out of the LoginActivity and placed it in the
LoginPresenter POJO. We can now create local JVM unit tests against our business logic.
It should be noted that there are various other implications from our change in architecture such as
we are close to adhering to each class having a single responsibility, additional classes, etc.
These are just side effects of the way I choose to go about performing this decoupling via the MVP
style. MVP is just one way to go about this but there are other alternatives that you may want to
look at such as MVVM. You just have to pick the best system that works for you.
Setup
To start unit testing your Android project using JUnit you need to add the JUnit dependency to
your project and you need to create a test source-set which is going to contain the source code for
the unit tests. Projects created with Android Studio often already include the JUnit dependency
and the test source-set
Add the following line to your module build.gradle file within the dependencies Closure:
testCompile 'junit:junit:4.12'
JUnit test classes are located in a special source-set named test. If this source-set does not exist
you need to create a new folder yourself. The folder structure of a default Android Studio (Gradle
based) project looks like this:
https://riptutorial.com/ 1408
<project-root-folder>
/app (module root folder)
/build
/libs
/src
/main (source code)
/test (unit test source code)
/androidTest (instrumentation test source code)
build.gradle (module gradle file)
/build
/gradle
build.gradle (project gradle file)
gradle.properties
gradlew
gradlew.bat
local.properties
settings.gradle (gradle settings)
If your project doesn't have the /app/src/test folder you need to create it yourself. Within the test
folder you also need a java folder (create it if it doesn't exist). The java folder in the test source set
should contains the same package structure as your main source-set.
If setup correctly your project structure (in the Android view in Android Studio) should look like this:
Note: You don't necessarily need to have the androidTest source-set, this source-set is often found
in projects created by Android Studio and is included here for reference.
Writing a test
1. Create a new class within the test source-set.
Right click the test source-set in the project view choose New > Java class.
The most used naming pattern is to use the name of the class you're going to test with Test
added to it. So StringUtilities becomes StringUtilitiesTest.
https://riptutorial.com/ 1409
@RunWith(JUnit4.class)
public class StringUtilitiesTest {
3. Create a test
A unit test is essentially just a method which, in most cases, should not fail if run. In other
words it should not throw an exception. Inside a test method you will almost always find
assertions that check if specific conditions are met. If an assertion fails it throws an exception
which causes the method/test to fail. A test method is always annotated with the @Test
annotation. Without this annotation JUnit won't automatically run the test.
@RunWith(JUnit4.class)
public class StringUtilitiesTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals("Hello JUnit", "Hello" + " " + "JUnit");
}
}
Note: unlike the standard Java method naming convention unit test method names do often
contain underscores.
Running a test
1. Method
To run a single test method you can right click the method and click Run
'addition_isCorrect()' or use the keyboard shortcut ctrl+shift+f10.
If everything is setup correctly JUnit starts running the method and you should see the
following interface within Android Studio:
https://riptutorial.com/ 1410
2. Class
You can also run all the tests defined in a single class, by right clicking the class in the
project view and clicking Run 'StringUtilitiesTest ' or use the keyboard shortcut
ctrl+shift+f10 if you have selected the class in the project view.
3. Package (everything)
If you wan't to run all the tests defined in the project or in a package you can just right click
the package and click Run ... just like you would run all the tests defined in a single class.
Exceptions
JUnit can also be used to test if a method throws a specific exception for a given input.
In this example we will test if the following method really throws an exception if the Boolean format
(input) is not recognized/unknown:
By adding the expected parameter to the @Test annotation, one can define which exception is
expected to be thrown. The unit test will fail if this exception does not occur, and succeed if the
exception is indeed thrown:
@Test(expected = IllegalArgumentException.class)
public void parseBoolean_parsesInvalidFormat_throwsException(){
StringUtilities.parseBoolean("Hello JUnit");
}
https://riptutorial.com/ 1411
This works well, however, it does limit you to just a single test case within the method. Sometimes
you might want to test multiple cases within a single method. A technique often used to overcome
this limitation is using try-catch blocks and the Assert.fail() method:
@Test
public void parseBoolean_parsesInvalidFormats_throwsException(){
try {
StringUtilities.parseBoolean("Hello!");
fail("Expected IllegalArgumentException");
} catch(IllegalArgumentException e){
}
try {
StringUtilities.parseBoolean("JUnit!");
fail("Expected IllegalArgumentException");
} catch(IllegalArgumentException e){
}
}
Note: Some people consider it to be bad practice to test more than a single case inside a unit test.
Static import
JUnit defines quite some assertEquals methods at least one for each primitive type and one for
Objects is available. These methods are by default not directly available to call and should be
called like this: Assert.assertEquals. But because these methods are used so often people almost
always use a static import so that the method can be directly used as if it is part of the class itself.
To add a static import for the assertEquals method use the following import statement:
You can also static import all assert methods including the assertArrayEquals, assertNotNull and
assertFalse etc. using the following static import:
@Test
public void addition_isCorrect(){
Assert.assertEquals(4 , 2 + 2);
}
@Test
public void addition_isCorrect(){
assertEquals(4 , 2 + 2);
}
https://riptutorial.com/ 1412
Read Unit testing in Android with JUnit online: https://riptutorial.com/android/topic/3205/unit-
testing-in-android-with-junit
https://riptutorial.com/ 1413
Chapter 251: Universal Image Loader
Remarks
Acceptable URI examples:
Examples
Initialize Universal Image Loader
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
3. Initialize the Universal Image Loader. This must be done before the first usage:
Basic usage
1. Load an image, decode it into a bitmap, and display the bitmap in an ImageView (or any other
view which implements the ImageAware interface):
ImageLoader.getInstance().displayImage(imageUri, imageView);
2. Load an image, decode it into a bitmap, and return the bitmap to a callback:
https://riptutorial.com/ 1414
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
// Do whatever you want with the bitmap.
}
});
3. Load an image, decode it into a bitmap and return the bitmap synchronously:
https://riptutorial.com/ 1415
Chapter 252: Unzip File in Android
Examples
Unzip file
fout.close();
zis.closeEntry();
}
zis.close();
}
catch(IOException e){
e.printStackTrace();
return false;
}
return true;}
https://riptutorial.com/ 1416
Chapter 253: Vector Drawables
Introduction
As the name implies, vector drawables are based on vector graphics. Vector graphics are a way of
describing graphical elements using geometric shapes. This lets you create a drawable based on
an XML vector graphic. Now there is no need to design different size image for mdpi, hdpi, xhdpi
and etc. With Vector Drawable you need to create image only once as an xml file and you can
scale it for all dpi and for different devices. This also not save space but also simplifies
maintenance.
Parameters
Parameter Details
<clip- Defines path to be the current clip. Note that the clip path only apply to the
path> current group and its children.
Remarks
Update build.gradle file.
dependencies {
...
compile 'com.android.support:appcompat-v7:23.2.1'
}
If you are using v2.0 or above of the Gradle plugin, then add following code.
If you are using v1.5 or below of the Gradle plugin, then add following code.
https://riptutorial.com/ 1417
// Gradle Plugin 1.5
android {
defaultConfig {
generatedDensities = []
}
Read Android Support Library 23.2 Release Notes for more info.
NOTE : Even with AppCompat, Vector Drawables wont work outside of your app in older android
versions. For instance, you cannot pass vector drawables as Notification icons as they are
handled by the system and not the app. See this answer for a workaround.
Examples
VectorDrawable Usage Example
res/drawable/ic_search.xml
<vector xmlns:android="..."
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?attr/colorControlNormal">
<path
android:pathData="..."
android:fillColor="@android:color/white"/>
</vector>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_search"/>
https://riptutorial.com/ 1418
VectorDrawable xml example
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="64dp"
android:width="64dp"
android:viewportHeight="600"
android:viewportWidth="600" >
<group
android:name="rotationGroup"
android:pivotX="300.0"
android:pivotY="300.0"
android:rotation="45.0" >
<path
android:name="v"
android:fillColor="#000000"
android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
</group>
</vector>
You can import an SVG file as a VectorDrawable in Android Studio, follow these steps :
"Right-click" on the res folder and select new > Vector Asset.
https://riptutorial.com/ 1419
Select the Local File option and browse to your .svg file. Change the options to your liking and hit
next. Done.
https://riptutorial.com/ 1420
Read Vector Drawables online: https://riptutorial.com/android/topic/8194/vector-drawables
https://riptutorial.com/ 1421
Chapter 254: VectorDrawable and
AnimatedVectorDrawable
Examples
Basic VectorDrawable
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M0,24 l12,-24 l12,24 z"/>
</vector>
Using
A <clip-path> defines a shape which acts as a window, only allowing parts of a <path> to show if
they are within the <clip-path> shape and cutting off the rest.
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<clip-path
https://riptutorial.com/ 1422
android:name="square clip path"
android:pathData="M6,6 h12 v12 h-12 z"/>
<path
android:name="triangle"
android:fillColor="#FF000000"
android:pathData="M0,24 l12,-24 l12,24 z"/>
</vector>
In this case the <path> produces a black triangle, but the <clip-path> defines a smaller square
shape, only allowing part of the triangle to show through:
tags
A <group> tag allows the scaling, rotation, and position of one or more elements of a VectorDrawable
to be adjusted:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:pathData="M0,0 h4 v4 h-4 z"
android:fillColor="#FF000000"/>
<group
android:name="middle square group"
android:translateX="10"
android:translateY="10"
android:rotation="45">
<path
android:pathData="M0,0 h4 v4 h-4 z"
android:fillColor="#FF000000"/>
</group>
<group
android:name="last square group"
android:translateX="18"
android:translateY="18"
https://riptutorial.com/ 1423
android:scaleX="1.5">
<path
android:pathData="M0,0 h4 v4 h-4 z"
android:fillColor="#FF000000"/>
</group>
</vector>
The example code above contains three identical <path> tags, all describing black squares. The
first square is unadjusted. The second square is wrapped in a <group> tag which moves it and
rotates it by 45°. The third square is wrapped in a <group> tag which moves it and stretches it
horizontally by 50%. The result is as follows:
A <group> tag can contain multiple <path> and <clip-path> tags. It can even contain another <group>.
Basic AnimatedVectorDrawable
The following creates a triangle that transitions its color from black to red.
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:name="triangle"
android:fillColor="@android:color/black"
android:pathData="M0,24 l12,-24 l12,24 z"/>
https://riptutorial.com/ 1424
</vector>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="fillColor"
android:duration="2000"
android:repeatCount="infinite"
android:valueFrom="@android:color/black"
android:valueTo="@android:color/holo_red_light"/>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/triangle_vector_drawable">
<target
android:animation="@animator/color_change_animator"
android:name="triangle"/>
</animated-vector>
Note that the <target> specifies android:name="triangle" which matches the <path> in the
VectorDrawable. A VectorDrawable may contain multiple elements and the android:name property is
used to define which element is being targeted.
Result:
Using Strokes
Using SVG stroke makes it easier to create a Vector drawable with unified stroke length, as per
Material Design guidelines:
Consistent stroke weights are key to unifying the overall system icon family. Maintain a
https://riptutorial.com/ 1425
2dp width for all stroke instances, including curves, angles, and both interior and
exterior strokes.
So, for example, this is how you would create a "plus" sign using strokes:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:strokeColor="#F000"
android:strokeWidth="2"
android:pathData="M12,0 V24 M0,12 H24" />
</vector>
• strokeWidth defines the width (in dp) of the stroke (2dp in this case, as suggested by the
guidelines).
etc., see SVG documentation and this useful "SVG Path" tutorial from w3schools to learn more
about the specific path commands.
https://riptutorial.com/ 1426
This is especially useful for creating an AnimatedVectorDrawable, since you are now operating with
a single stroke with an unified length, instead of an otherwise complicated path.
A few pre-requisites in the build.gradle for vectors to work all the way down to API 7 for
VectorDrawables and API 13 for AnimatedVectorDrawables (with some caveats currently):
defaultConfig {
vectorDrawables.useSupportLibrary = true
generatedDensities = []
aaptOptions {
additionalParameters "--no-version-vectors"
}
}
dependencies {
compile 'com.android.support:appcompat-v7:24.1.1'
}
In your layout.xml:
<ImageView
android:id="@+id/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
appCompat:src="@drawable/vector_drawable"
android:contentDescription="@null" />
https://riptutorial.com/ 1427
Read VectorDrawable and AnimatedVectorDrawable online:
https://riptutorial.com/android/topic/1627/vectordrawable-and-animatedvectordrawable
https://riptutorial.com/ 1428
Chapter 255: Vibration
Examples
Getting Started with Vibration
before you start implement code, you have to add permission in android manifest :
<uses-permission android:name="android.permission.VIBRATE"/>
import android.os.Vibrator;
Vibrate Indefinitely
Vibration Patterns
You can create vibration patterns by passing in an array of longs, each of which represents a
duration in milliseconds. The first number is start time delay. Each array entry then alternates
between vibrate, sleep, vibrate, sleep, etc.
https://riptutorial.com/ 1429
The following example demonstrates this pattern:
To cause the pattern to repeat, pass in the index into the pattern array at which to start the repeat,
or -1 to disable repeating.
Stop Vibrate
vibrator.cancel();
https://riptutorial.com/ 1430
Chapter 256: VideoView
Examples
VideoView Create
videoView.start();
<VideoView
android:id="@+id/videoView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
videoView.setVideoURI(Uri.parse("http://example.com/examplevideo.mp4"));
videoView.requestFocus();
videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
}
});
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
videoView.start();
mediaPlayer.setOnVideoSizeChangedListener(new
MediaPlayer.OnVideoSizeChangedListener() {
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
MediaController mediaController = new
MediaController(ActivityName.this);
videoView.setMediaController(mediaController);
mediaController.setAnchorView(videoView);
}
});
}
});
videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
https://riptutorial.com/ 1431
@Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
return false;
}
});
https://riptutorial.com/ 1432
Chapter 257: ViewFlipper
Introduction
A ViewFlipper is a ViewAnimator that switches between two or more views that have been added to
it. Only one child is shown at a time. If requested, the ViewFlipper can automatically flip between
each child at a regular interval.
Examples
ViewFlipper with image sliding
XML file:
<ViewFlipper
android:id="@+id/viewflip"
android:layout_width="match_parent"
android:layout_height="250dp"
android:layout_weight="1"
/>
JAVA code:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState){
View rootView = inflater.inflate(fragment_blank, container, false);
viewFlipper = (ViewFlipper)rootView.findViewById(R.id.viewflip);
for(int i=0; i<gallery_grid_Images.length; i++){
// This will create dynamic image views and add them to the ViewFlipper.
setFlipperImage(gallery_grid_Images[i]);
}
return rootView;
}
https://riptutorial.com/ 1433
Read ViewFlipper online: https://riptutorial.com/android/topic/9032/viewflipper
https://riptutorial.com/ 1434
Chapter 258: ViewPager
Introduction
ViewPager is a Layout manager that allows the user to flip left and right through pages of data. It
is most often used in conjunction with Fragment, which is a convenient way to supply and manage
the lifecycle of each page.
Remarks
One important thing to note about ViewPager usage is that there are two different versions of both
FragmentPagerAdapter and FragmentStatePagerAdapter.
Examples
Basic ViewPager usage with fragments
A ViewPager allows to show multiple fragments in an activity that can be navigated by either fliping
left or right. A ViewPager needs to be feed of either Views or Fragments by using a PagerAdapter.
There are however two more specific implementations that you will find most useful in case of
using Fragments which are FragmentPagerAdapter and FragmentStatePagerAdapter. When a Fragment
needs to be instantiated for the first time, getItem(position) will be called for each position that
needs instantiating. The getCount() method will return the total number of pages so the ViewPager
knows how many Fragments need to be shown.
Both FragmentPagerAdapter and FragmentStatePagerAdapter keep a cache of the Fragments that the
ViewPager will need to show. By default the ViewPager will try to store a maximum of 3 Fragments
that correspond to the currently visible Fragment, and the ones next to the right and left. Also
FragmentStatePagerAdapter will keep the state of each of your fragments.
Be aware that both implementations assume your fragments will keep their positions, so if you
keep a list of the fragments instead of having a static number of them as you can see in the
getItem() method, you will need to create a subclass of PagerAdapter and override at least
instantiateItem(),destroyItem() and getItemPosition()methods.
https://riptutorial.com/ 1435
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout>
<android.support.v4.view.ViewPager
android:id="@+id/vpPager">
</android.support.v4.view.ViewPager>
</LinearLayout>
Then define the adapter that will determine how many pages exist and which fragment to display
for each page of the adapter.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myActivityLayout);
case 1:
return new Fragment2();
case 2:
return new Fragment3();
default:
return null;
}
}
}
}
https://riptutorial.com/ 1436
3.2.x
compile 'com.android.support:support-v13:25.3.1'
compile 'com.android.support:support-fragment:25.3.1'
This method will sync by creating and removing tabs according to the contents of the adapter
associated with your ViewPager each time you call it.
Also, it will set a callback so each time the user flips the page, the corresponding tab will be
selected.
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
app:tabMode="scrollable" />
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1" />
</LinearLayout>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myActivityLayout);
https://riptutorial.com/ 1437
mFragmentAdapter = new MyPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.view_pager);
mViewPager.setAdapter(mFragmentAdapter);
case 1:
return new Fragment2();
case 2:
return new Fragment3();
default:
return null;
}
}
case 1:
return "Fragment 2 title";
case 2:
return "Fragment 3 title";
default:
return null;
}
}
}
}
https://riptutorial.com/ 1438
ViewPager with PreferenceFragment
The latest versions of the support v7 library now include the PreferenceFragmentCompat class, which
will work with a ViewPager and the v4 version of FragmentPagerAdapter.
import android.os.Bundle;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.view.View;
public MySettingsPrefFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.fragment_settings_pref);
}
@Override
public void onCreatePreferences(Bundle bundle, String s) {
}
}
@Override
public Fragment getItem(int position) {
switch(position) {
case 0:
return new FragmentOne();
case 1:
return new FragmentTwo();
case 2:
return new MySettingsPrefFragment();
default:
return null;
}
}
https://riptutorial.com/ 1439
// .......
Adding a ViewPager
Make sure the following dependency is added to your app's build.gradle file under dependencies:
compile 'com.android.support:support-core-ui:25.3.0'
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
@Override
public Object instantiateItem(ViewGroup collection, int position) {
@Override
public void destroyItem(ViewGroup collection, int position, Object view) {
// Remove a page for the given position. For example:
collection.removeView((View) view);
}
@Override
public int getCount() {
//Return the number of views available.
return numberOfPages;
}
@Override
public boolean isViewFromObject(View view, Object object) {
// Determines whether a page View is associated with a specific key object
// as returned by instantiateItem(ViewGroup, int). For example:
return view == object;
}
https://riptutorial.com/ 1440
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
All we need are: ViewPager, TabLayout and 2 drawables for selected and default dots.
Firstly, we have to add TabLayout to our screen layout, and connect it with ViewPager. We can do
this in two ways:
<android.support.v4.view.ViewPager
android:id="@+id/photos_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
https://riptutorial.com/ 1441
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.v4.view.ViewPager>
In this case TabLayout will be automatically connected with ViewPager, but TabLayout will
be next to ViewPager, not over him.
Separate TabLayout
<android.support.v4.view.ViewPager
android:id="@+id/photos_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
In this case, we can put TabLayout anywhere, but we have to connect TabLayout with
ViewPager programmatically
Once we created our layout, we have to prepare our dots. So we create three files:
selected_dot.xml, default_dot.xml and tab_selector.xml.
selected_dot.xml
default_dot.xml
https://riptutorial.com/ 1442
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="8dp"
android:useLevel="false">
<solid android:color="@android:color/darker_gray"/>
</shape>
</item>
</layer-list>
tab_selector.xml
<item android:drawable="@drawable/selected_dot"
android:state_selected="true"/>
<item android:drawable="@drawable/default_dot"/>
</selector>
Now we need to add only 3 lines of code to TabLayout in our xml layout and you're done.
app:tabBackground="@drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
Setup OnPageChangeListener
If you need to listen for changes to the page selected you can implement the
ViewPager.OnPageChangeListener listener on the ViewPager:
viewPager.addOnPageChangeListener(new OnPageChangeListener() {
// This method will be invoked when a new page becomes selected. Animation is not
necessarily complete.
@Override
public void onPageSelected(int position) {
// Your code
}
// This method will be invoked when the current page is scrolled, either as part of
// a programmatically initiated smooth scroll or a user initiated touch scroll.
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// Your code
}
// Called when the scroll state changes. Useful for discovering when the user begins
// dragging, when the pager is automatically settling to the current page,
https://riptutorial.com/ 1443
// or when it is fully stopped/idle.
@Override
public void onPageScrollStateChanged(int state) {
// Your code
}
});
https://riptutorial.com/ 1444
Chapter 259: Volley
Introduction
Volley is an Android HTTP library that was introduced by Google to make networking calls much
simpler. By default all the Volley network calls are made asynchronously, handling everything in a
background thread and returning the results in the foreground with use of callbacks. As fetching
data over a network is one of the most common tasks that is performed in any app, the Volley
library was made to ease Android app development.
Syntax
• RequestQueue queue = Volley.newRequestQueue(context); // setup the queue
• Request request = new SomeKindOfRequestClass(Request.Method, String url,
Response.Listener, Response.ErrorListener); // setup some kind of request, the exact type
and arguments change for each request type
• queue.add(request); // add the request to the queue; the appropriate response listener will
be called once the request is finished (or terminated for whatever reason)
Remarks
Installation
You can build Volley from the official Google source code. For a while, that was the only option. Or
using one of the third-party pre-built versions. However, Google finally released an official maven
package on jcenter.
dependencies {
...
compile 'com.android.volley:volley:1.0.0'
}
<uses-permission android:name="android.permission.INTERNET"/>
Official Documentation
Google has not provided very extensive documentation on this library, and they haven't touched it
in years. But what is available can be found at:
https://riptutorial.com/ 1445
https://developer.android.com/training/volley/index.html
There is unofficial documentation hosted on GitHub, although there should be a better location to
host this in the future:
https://pablobaxter.github.io/volley-docs/
Examples
Basic StringRequest using GET method
Cancel a request
// assume a Request and RequestQueue have already been initialized somewhere above
// ... then, in some future life cycle event, for example in onStop()
// To cancel all requests with the specified tag in RequestQueue
mRequestQueue.cancelAll(TAG);
https://riptutorial.com/ 1446
Adding custom design time attributes to NetworkImageView
There are several additional attributes that the Volley NetworkImageView adds to the standard
ImageView. However, these attributes can only be set in code. The following is an example of how
to make an extension class that will pick up the attributes from your XML layout file and apply
them to the NetworkImageView instance for you.
<resources>
<declare-styleable name="MoreNetworkImageView">
<attr name="defaultImageResId" format="reference"/>
<attr name="errorImageResId" format="reference"/>
</declare-styleable>
</resources>
package my.namespace;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import com.android.volley.toolbox.NetworkImageView;
https://riptutorial.com/ 1447
}
}
<my.namespace.MoreNetworkImageView
android:layout_width="64dp"
android:layout_height="64dp"
app:errorImageResId="@drawable/error_img"
app:defaultImageResId="@drawable/default_img"
tools:defaultImageResId="@drawable/editor_only_default_img"/>
<!--
Note: The "tools:" prefix does NOT work for custom attributes in Android Studio 2.1 and
older at least, so in this example the defaultImageResId would show "default_img" in the
</android.support.v7.widget.CardView>
Request JSON
requestQueue.add(jsObjRequest);
If you need to add custom headers to your volley requests, you can't do this after initialisation, as
the headers are saved in a private variable.
https://riptutorial.com/ 1448
new JsonObjectRequest(REQUEST_METHOD, REQUEST_URL, REQUEST_BODY, RESP_LISTENER, ERR_LISTENER)
{
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> customHeaders = new Hashmap<>();
customHeaders.put("KEY_0", "VALUE_0");
...
customHeaders.put("KEY_N", "VALUE_N");
return customHeaders;
}
};
If you want to build a custom request, you can add the headers in it as well:
customHeaders.put("KEY_0", "VALUE_0");
...
customHeaders.put("KEY_N", "VALUE_N");
return customHeaders;
}
...
}
https://riptutorial.com/ 1449
return context.getResources().getString(R.string.timeout);
}else if (isServerProblem(error)){
return handleServerError(error ,context);
}else if(isNetworkProblem(error)){
return context.getResources().getString(R.string.nointernet);
}
return context.getResources().getString(R.string.generic_error);
VolleyError er = (VolleyError)error;
NetworkResponse response = er.networkResponse;
if(response != null){
switch (response.statusCode){
case 404:
case 422:
case 401:
try {
// server might return error like this { "error": "Some error
occured" }
// Use "Gson" to parse the result
HashMap<String, String> result = new Gson().fromJson(new
String(response.data),
new TypeToken<Map<String, String>>() {
}.getType());
} catch (Exception e) {
e.printStackTrace();
}
// invalid request
return ((VolleyError) error).getMessage();
default:
return context.getResources().getString(R.string.timeout);
}
}
return context.getResources().getString(R.string.generic_error);
}
For the sake of this example, let us assume that we have a server for handling the POST requests
https://riptutorial.com/ 1450
that we will be making from our Android app:
// We will need key ids for our data, so our server can know
// what is what.
String key_email = "email";
String key_password = "password";
https://riptutorial.com/ 1451
public int getCurrentTimeout() {
// Here goes the timeout.
// The number is in milliseconds, 5000 is usually enough,
// but you can up or low that number to fit your needs.
return 50000;
}
@Override
public int getCurrentRetryCount() {
// The maximum number of attempts.
// Again, the number can be anything you need.
return 50000;
}
@Override
public void retry(VolleyError error) throws VolleyError {
// Here you could check if the retry count has gotten
// to the maximum number, and if so, send a VolleyError
// message or similar. For the sake of the example, I'll
// show a Toast.
Toast.makeText(getContext(), error.toString(), Toast.LENGTH_LONG).show();
}
});
// And finally, we create a Volley Queue. For this example, I'm using
// getContext(), because I was working with a Fragment. But context could
// be "this", "getContext()", etc.
RequestQueue requestQueue = Volley.newRequestQueue(getContext());
requestQueue.add(stringRequest);
} else {
// If, for example, the user inputs an email that is not currently
// on your remote DB, here's where we can inform the user.
Toast.makeText(getContext(), "Wrong email", Toast.LENGTH_LONG).show();
}
compile 'com.android.volley:volley:1.0.0'
@Override
public void onCreate() {
super.onCreate();
sInstance = this;
https://riptutorial.com/ 1452
Stetho.initializeWithDefaults(this);
Now, you can use the volley instance using the getInstance() method and add a new request in
the queue using InitApplication.getInstance().addToQueue(request);
@Override
public void onResponse(JSONObject response) {
Log.d(TAG, response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "Error: " + error.getMessage());
}
});
myRequest.setRetryPolicy(new DefaultRetryPolicy(
MY_SOCKET_TIMEOUT_MS,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
https://riptutorial.com/ 1453
To handle Volley timeouts you need to use a RetryPolicy. A retry policy is used in case a request
cannot be completed due to network failure or some other cases.
Volley provides an easy way to implement your RetryPolicy for your requests. By default, Volley
sets all socket and connection timeouts to 5 seconds for all requests. RetryPolicy is an interface
where you need to implement your logic of how you want to retry a particular request when a
timeout occurs.
• initialTimeoutMs - Specifies the socket timeout in milliseconds for every retry attempt.
• maxNumRetries - The number of times retry is attempted.
• backoffMultiplier - A multiplier which is used to determine exponential time set to socket for
every retry attempt.
@Override
protected Response<Boolean> parseNetworkResponse(NetworkResponse response) {
Boolean parsed;
try {
parsed = Boolean.valueOf(new String(response.data,
HttpHeaderParser.parseCharset(response.headers)));
} catch (UnsupportedEncodingException e) {
parsed = Boolean.valueOf(new String(response.data));
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
@Override
protected VolleyError parseNetworkError(VolleyError volleyError) {
return super.parseNetworkError(volleyError);
}
@Override
protected void deliverResponse(Boolean response) {
mListener.onResponse(response);
}
@Override
public void deliverError(VolleyError error) {
mErrorListener.onErrorResponse(error);
}
https://riptutorial.com/ 1454
@Override
public String getBodyContentType() {
return PROTOCOL_CONTENT_TYPE;
}
@Override
public byte[] getBody() throws AuthFailureError {
try {
return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
mRequestBody, PROTOCOL_CHARSET);
return null;
}
}
}
try {
JSONObject jsonBody;
jsonBody = new JSONObject();
jsonBody.put("Title", "Android Demo");
jsonBody.put("Author", "BNK");
jsonBody.put("Date", "2015/08/28");
String requestBody = jsonBody.toString();
BooleanRequest booleanRequest = new BooleanRequest(0, url, requestBody, new
Response.Listener<Boolean>() {
@Override
public void onResponse(Boolean response) {
Toast.makeText(mContext, String.valueOf(response), Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(mContext, error.toString(), Toast.LENGTH_SHORT).show();
}
});
// Add the request to the RequestQueue.
queue.add(booleanRequest);
} catch (JSONException e) {
e.printStackTrace();
}
The default requests integrated in volley don't allow to pass a JSONArray as request body in a POST
request. Instead, you can only pass a JSON object as a parameter.
However, instead of passing a JSON object as a parameter to the request constructor, you need to
override the getBody() method of the Request.class. You should pass null as third parameter as
well:
https://riptutorial.com/ 1455
new JsonObjectRequest(Request.Method.POST, REQUEST_URL, null, RESP_LISTENER, ERR_LISTENER) {
@Override
public byte[] getBody() {
try {
return requestBody.toString().getBytes(PROTOCOL_CHARSET);
} catch (UnsupportedEncodingException uee) {
// error handling
return null;
}
}
};
https://riptutorial.com/ 1456
Chapter 260: WebView
Introduction
WebView is a view that display web pages inside your application. By this you can add your own
URL.
Remarks
Please don't forget to add permission in your Android manifest file
Examples
JavaScript alert dialogs in WebView - How to make them work
By default, WebView does not implement JavaScript alert dialogs, ie. alert() will do nothing. In
order to make you need to firstly enable JavaScript (obviously..), and then set a WebChromeClient to
handle requests for alert dialogs from the page:
webView.setWebChromeClient(new WebChromeClient() {
//Other methods for your WebChromeClient here, if needed..
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
});
Here, we override onJsAlert, and then we call through to the super implementation, which gives us
a standard Android dialog. You can also use the message and URL yourself, for example if you
want to create a custom styled dialog or if you want to log them.
Android Activity
package com.example.myapp;
import android.os.Bundle;
import android.app.Activity;
import android.webkit.WebView;
https://riptutorial.com/ 1457
super.onCreate(savedInstanceState);
/*
* Note the label Android, this is used in the Javascript side of things
* You can of course change this.
*/
webView.addJavascriptInterface(new JavascriptHandler(), "Android");
webView.loadUrl("http://example.com");
}
}
import android.webkit.JavascriptInterface;
/**
* Key point here is the annotation @JavascriptInterface
*
*/
@JavascriptInterface
public void jsCallback() {
// Do something
}
@JavascriptInterface
public void jsCallbackTwo(String dummyData) {
// Do something
}
}
<script>
...
Android.jsCallback();
...
Android.jsCallback('hello test');
...
</script>
Extra Tip
On the Android side use your favorite JSON parser ie: JSONObject
https://riptutorial.com/ 1458
Basic Example
package com.example.myapp;
import android.os.Bundle;
import android.app.Activity;
import android.webkit.WebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(webView);
webView.loadUrl("http://example.com");
/*
* Invoke Javascript function
*/
webView.loadUrl("javascript:testJsFunction('Hello World!')");
}
/**
* Invoking a Javascript function
*/
public void doSomething() {
this.webView.loadUrl("javascript:testAnotherFunction('Hello World Again!')");
}
}
If the web page a contains phone number you can make a call using your phone's dialer. This
code checks for the url which starts with tel: then make an intent to open dialer and you can make
a call to the clicked phone number:
https://riptutorial.com/ 1459
Printing webview console messages to logcat
To handle console messages from web page you can override onConsoleMessage in WebChromeClient:
webView.setWebChromeClient(new ChromeClient());
<html>
<head>
<script type="text/javascript">
console.log('test message');
</script>
</head>
<body>
</body>
</html>
On your Android device, open up Settings, find the Developer options section, and enable USB
debugging.
https://riptutorial.com/ 1460
From the Inspect Devices dialog, select your device and press inspect. A new instance of
Chrome's DevTools opens up on your development machine.
Layout.xml
<WebView
android:id="@+id/WebViewToDisplay"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:fadeScrollbars="false" />
WebView webViewDisplay;
StringBuffer LoadWEb1;
https://riptutorial.com/ 1461
Chapter 261: What is ProGuard? What is use
in Android?
Introduction
Proguard is free Java class file shrinker, optimizer, obfuscator, and preverifier. It detects and
removes unused classes, fields, methods, and attributes. It optimizes bytecode and removes
unused instructions. It renames the remaining classes, fields, and methods using short
meaningless names.
Examples
Shrink your code and resources with proguard
To make your APK file as small as possible, you should enable shrinking to remove unused code
and resources in your release build. This page describes how to do that and how to specify what
code and resources to keep or discard during the build.
Code shrinking is available with ProGuard, which detects and removes unused classes, fields,
methods, and attributes from your packaged app, including those from included code libraries
(making it a valuable tool for working around the 64k reference limit). ProGuard also optimizes the
bytecode, removes unused code instructions, and obfuscates the remaining classes, fields, and
methods with short names. The obfuscated code makes your APK difficult to reverse engineer,
which is especially valuable when your app uses security-sensitive features, such as licensing
verification.
Resource shrinking is available with the Android plugin for Gradle, which removes unused
resources from your packaged app, including unused resources in code libraries. It works in
conjunction with code shrinking such that once unused code has been removed, any resources no
longer referenced can be safely removed as well.
To enable code shrinking with ProGuard, add minifyEnabled true to the appropriate build type in your
build.gradle file.
Be aware that code shrinking slows down the build time, so you should avoid using it on your
debug build if possible. However, it's important that you do enable code shrinking on your final
APK used for testing, because it might introduce bugs if you do not sufficiently customize which
code to keep.
For example, the following snippet from a build.gradle file enables code shrinking for the release
build:
android {
https://riptutorial.com/ 1462
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
...
}
In addition to the minifyEnabled property, the proguardFiles property defines the ProGuard rules:
android {
...
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
productFlavors {
flavor1 {
}
flavor2 {
proguardFile 'flavor2-rules.pro'
}
}
}
https://riptutorial.com/ 1463
Chapter 262: Widgets
Remarks
SDv
Examples
Manifest Declaration -
Declare the AppWidgetProvider class in your application's AndroidManifest.xml file. For example:
Metadata
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp"
android:minHeight="40dp"
android:updatePeriodMillis="86400000"
android:previewImage="@drawable/preview"
android:initialLayout="@layout/example_appwidget"
android:configure="com.example.android.ExampleAppWidgetConfigure"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen">
</appwidget-provider>
AppWidgetProvider Class
// Perform this loop procedure for each App Widget that belongs to this provider
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
https://riptutorial.com/ 1464
// Create an Intent to launch ExampleActivity
Intent intent = new Intent(context, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
// Get the layout for the App Widget and attach an on-click listener
// to the button
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
<receiver
android:name=".UVMateWidget"
android:label="UVMate Widget 1x1">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_1x1" />
</receiver>
<receiver
android:name=".UVMateWidget2x2"
android:label="UVMate Widget 2x2">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_2x2" />
</receiver>
package au.com.aershov.uvmate;
import android.content.Context;
import android.widget.RemoteViews;
https://riptutorial.com/ 1465
public class UVMateWidget2x2 extends UVMateWidget {
mUVMateHelper.saveWidgetSize(mContext.getString(R.string.app_ws_2x2));
return new RemoteViews(context.getPackageName(), R.layout.widget_2x2);
}
}
Latest Android Studio will create & integrate a Basic Widget to your Application in 2 steps.
https://riptutorial.com/ 1466
Its Done.
It will create & integrate a basic HelloWorld Widget(Including Layout File , Meta Data File ,
Declaration in Manifest File etc.) to your Application.
https://riptutorial.com/ 1467
Chapter 263: Wi-Fi Connections
Examples
Connect with WEP encryption
This example connects to a Wi-Fi access point with WEP encryption, given an SSID and the
password.
conf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
conf.allowedGroupCiphers.set(WifiConfiguration.AuthAlgorithm.OPEN);
conf.allowedGroupCiphers.set(WifiConfiguration.AuthAlgorithm.SHARED);
if (networkId == -1){
//Try it again with no quotes in case of hex password
conf.wepKeys[0] = password;
networkId = wifiManager.addNetwork(conf);
}
https://riptutorial.com/ 1468
try {
WifiConfiguration conf = new WifiConfiguration();
conf.SSID = "\"" + networkSSID + "\""; // Please note the quotes. String should contain
SSID in quotes
conf.status = WifiConfiguration.Status.ENABLED;
conf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
conf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
conf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
conf.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
conf.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
break;
}
}
This example scans for available access points and ad hoc networks. btnScan activates a scan
initiated by the WifiManager.startScan() method. After the scan, WifiManager calls the
SCAN_RESULTS_AVAILABLE_ACTION intent and the WifiScanReceiver class processes the scan result. The
results are displayed in a TextView.
TextView txtWifiInfo;
WifiManager wifi;
WifiScanReceiver wifiReceiver;
@Override
https://riptutorial.com/ 1469
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
wifi=(WifiManager)getSystemService(Context.WIFI_SERVICE);
wifiReceiver = new WifiScanReceiver();
txtWifiInfo = (TextView)findViewById(R.id.txtWifiInfo);
Button btnScan = (Button)findViewById(R.id.btnScan);
btnScan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "Start scan...");
wifi.startScan();
}
});
}
Permissions
When compiling the project for api level 23 or greater (Android 6.0 and up), either
android.permission.ACCESS_FINE_LOCATION or android.permission.ACCESS_COARSE_LOCATION must be
inserted. Furthermore that permission needs to be requested, e.g. in the onCreate method of your
main activity:
https://riptutorial.com/ 1470
@Override
protected void onCreate(Bundle savedInstanceState) {
...
String[] PERMS_INITIAL={
Manifest.permission.ACCESS_FINE_LOCATION,
};
ActivityCompat.requestPermissions(this, PERMS_INITIAL, 127);
}
https://riptutorial.com/ 1471
Chapter 264: Writing UI tests - Android
Introduction
Focus of this document is to represent goals and ways how to write android UI and integration
tests. Espresso and UIAutomator are provided by Google so focus should be around these tools
and their respective wrappers e.g. Appium, Spoon etc.
Syntax
• Idling resource
• String getName() - Returns the name of the idling resource(used for logging and
idempotency of registration).
• boolean isIdleNow() - Returns true if resource is currently idle.
• void registerIdleTransitionCallback(IdlingResource.ResourceCallback callback) - Registers
the given IdlingResource.ResourceCallback with the resource
Remarks
JUnit rules:
As you can see in MockWebServer example and ActivityTestRule they all fall under category of
JUnit rules which you can create yourself which then should be executed for each test defining its
behaviour @see: https://github.com/junit-team/junit4/wiki/rules
Appium
Parameters
Since parameters have some issues placing them here until documentation bug is resolved:
Parameter Details
Class
which activity to start
activityClass
true if the Activity should be launched once per Test method. It will be
launchActivity launched before the first Before method, and terminated after the last
After method.
https://riptutorial.com/ 1472
Examples
MockWebServer example
In case your activities, fragments and UI require some background processing a good thing to use
is a MockWebServer which runs localy on an android device which brings a closed and testable
enviroment for your UI.
https://github.com/square/okhttp/tree/master/mockwebserver
Now steps for running and using the mock server are:
This is nicely explained in the github page of the mockwebserver but in our case we want
something nicer and reusable for all tests, and JUnit rules will come nicely into play here:
/**
*JUnit rule that starts and stops a mock web server for test runner
*/
public class MockServerRule extends UiThreadTestRule {
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
startServer();
try {
base.evaluate();
} finally {
stopServer();
}
}
};
}
/**
* Returns the started web server instance
*
* @return mock server
*/
public MockWebServer server() {
https://riptutorial.com/ 1473
return mServer;
}
Now lets assume that we have the exact same activity like in previous example, just in this case
when we push the button app will fetch something from the network for example:
https://someapi.com/name
This would return some text string which would be concatenated in the snackbar text e.g. NAME +
text you typed in.
/**
* Testing of the snackbar activity with networking.
**/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class SnackbarActivityTest{
//espresso rule which tells which activity to start
@Rule
public final ActivityTestRule<SnackbarActivity> mActivityRule =
new ActivityTestRule<>(SnackbarActivity.class, true, false);
@Override
public void tearDown() throws Exception {
//same as previous example
}
@Override
public void setUp() throws Exception {
//same as previous example
/**
*Test methods should always start with "testXYZ" and it is a good idea to
*name them after the intent what you want to test
https://riptutorial.com/ 1474
**/
@Test
public void testSnackbarIsShown() {
//setup mockweb server
mMockServerRule.server().setDispatcher(getDispatcher());
mActivityRule.launchActivity(null);
//check is our text entry displayed and enter some text to it
String textToType="new snackbar text";
onView(withId(R.id.textEntry)).check(matches(isDisplayed()));
//we check is our snackbar showing text from mock webserver plus the one we typed
onView(withId(R.id.textEntry)).perform(typeText("JazzJackTheRabbit" + textToType));
//click the button to show the snackbar
onView(withId(R.id.shownSnackbarBtn)).perform(click());
//assert that a view with snackbar_id with text which we typed and is displayed
onView(allOf(withId(android.support.design.R.id.snackbar_text),
withText(textToType))) .check(matches(isDisplayed()));
}
/**
*creates a mock web server dispatcher with prerecorded requests and responses
**/
private Dispatcher getDispatcher() {
final Dispatcher dispatcher = new Dispatcher() {
@Override
public MockResponse dispatch(RecordedRequest request) throws InterruptedException
{
if (request.getPath().equals("/name")){
return new MockResponse().setResponseCode(200)
.setBody("JazzJackTheRabbit");
}
throw new IllegalStateException("no mock set up for " + request.getPath());
}
};
return dispatcher;
}
I would suggest wrapping the dispatcher in some sort of a Builder so you can easily chain and add
new responses for your screens. e.g.
return newDispatcherBuilder()
.withSerializedJSONBody("/authenticate", Mocks.getAuthenticationResponse())
.withSerializedJSONBody("/getUserInfo", Mocks.getUserInfo())
.withSerializedJSONBody("/checkNotBot", Mocks.checkNotBot());
IdlingResource
The power of idling resources lies in not having to wait for some app's processing (networking,
calculations, animations, etc.) to finish with sleep(), which brings flakiness and/or prolongs the
tests run. The official documentation can be found here.
Implementation
There are three things that you need to do when implementing IdlingResource interface:
https://riptutorial.com/ 1475
• getName() - Returns the name of your idling resource.
• isIdleNow() - Checks whether your xyz object, operation, etc. is idle at the moment.
• registerIdleTransitionCallback (IdlingResource.ResourceCallback callback) - Provides a
callback which you should call when your object transitions to idle.
Now you should create your own logic and determine when your app is idle and when not, since
this is dependant on the app. Below you will find a simple example, just to show how it works.
There are other examples online, but specific app implementation brings to specific idling resource
implementations.
NOTES
• There have been some Google examples where they put IdlingResources in the code of the
app. Do not do this. They presumably placed it there just to show how they work.
• Keeping your code clean and maintaining single principle of responsibility is up to you!
Example
Let us say that you have an activity which does weird stuff and takes a long time for the fragment
to load and thus making your Espresso tests fail by not being able to find resources from your
fragment (you should change how your activity is created and when to speed it up). But in any
case to keep it simple, the following example shows how it should look like.
• The tag of the fragment which you need to find and waiting to get attached to the activity.
• A FragmentManager object which is used for finding the fragment.
/**
* FragmentIdlingResource - idling resource which waits while Fragment has not been loaded.
*/
public class FragmentIdlingResource implements IdlingResource {
private final FragmentManager mFragmentManager;
private final String mTag;
//resource callback you use when your activity transitions to idle
private volatile ResourceCallback resourceCallback;
@Override
public String getName() {
return FragmentIdlingResource.class.getName() + ":" + mTag;
}
@Override
public boolean isIdleNow() {
//simple check, if your fragment is added, then your app has became idle
boolean idle = (mFragmentManager.findFragmentByTag(mTag) != null);
https://riptutorial.com/ 1476
if (idle) {
//IMPORTANT: make sure you call onTransitionToIdle
resourceCallback.onTransitionToIdle();
}
return idle;
}
@Override
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
this.resourceCallback = resourceCallback;
}
}
Now that you have your IdlingResource written, you need to use it somewhere right?
Usage
Let us skip the entire test class setup and just look how a test case would look like:
@Test
public void testSomeFragmentText() {
mActivityTestRule.launchActivity(null);
@Rule
public final SDKIdlingRule mSdkIdlingRule = new
SDKIdlingRule(SDKInstanceHolder.getInstance());
Now since this is an example, don't take it for granted; all code here is imaginary and used only for
demonstration purposes:
https://riptutorial.com/ 1477
private VolleyIdlingResource mVolleyIdlingResource;
//request queue that you need from volley to give it to idling resource
private RequestQueue mRequestQueue;
//when using the rule extract the request queue from your SDK
public SDKIdlingRule(SDKClass sdkClass) {
mRequestQueue = getVolleyRequestQueue(sdkClass);
}
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
//registering idling resource
mVolleyIdlingResource = new VolleyIdlingResource(mRequestQueue);
Espresso.registerIdlingResources(mVolleyIdlingResource);
try {
base.evaluate();
} finally {
if (mVolleyIdlingResource != null) {
//deregister the resource when test finishes
Espresso.unregisterIdlingResources(mVolleyIdlingResource);
}
}
}
};
}
}
https://riptutorial.com/ 1478
Chapter 265: XMPP register login and chat
simple example
Examples
XMPP register login and chat basic example
Install openfire or any chat server in your system or on server. For more details click here.
compile 'org.igniterealtime.smack:smack-android:4.2.0'
compile 'org.igniterealtime.smack:smack-tcp:4.2.0'
compile 'org.igniterealtime.smack:smack-im:4.2.0'
compile 'org.igniterealtime.smack:smack-android-extensions:4.2.0'
builder.setHost(HOST);
builder.setPort(PORT);
builder.setCompressionEnabled(false);
builder.setDebuggerEnabled(true);
builder.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
builder.setSendPresence(true);
https://riptutorial.com/ 1479
DomainBareJid serviceName = JidCreate.domainBareFrom(HOST);
builder.setServiceName(serviceName);
return builder.build();
}
long l = System.currentTimeMillis();
try {
if(this.connection != null){
Log.logDebug(TAG, "Connection found, trying to connect");
this.connection.connect();
}else{
Log.logDebug(TAG, "No Connection found, trying to create a new connection");
XMPPTCPConnectionConfiguration config = buildConfiguration();
SmackConfiguration.DEBUG = true;
this.connection = new XMPPTCPConnection(config);
this.connection.connect();
}
} catch (Exception e) {
Log.logError(TAG,"some issue with getting connection :" + e.getMessage());
https://riptutorial.com/ 1480
try {
// Save username and password then use here
String username = AppSettings.getUser(context);
String password = AppSettings.getPassword(context);
this.connection = getConnection();
Log.logDebug(TAG, "XMPP username :" + username);
Log.logDebug(TAG, "XMPP password :" + password);
this.connection.login(username, password);
Log.logDebug(TAG, "Connect and Login method, Login successful");
context.sendBroadcast(new Intent(ACTION_LOGGED_IN));
} catch (XMPPException localXMPPException) {
Log.logError(TAG, "Error in Connect and Login Method");
localXMPPException.printStackTrace();
} catch (SmackException e) {
Log.logError(TAG, "Error in Connect and Login Method");
e.printStackTrace();
} catch (IOException e) {
Log.logError(TAG, "Error in Connect and Login Method");
e.printStackTrace();
} catch (InterruptedException e) {
Log.logError(TAG, "Error in Connect and Login Method");
e.printStackTrace();
} catch (IllegalArgumentException e) {
Log.logError(TAG, "Error in Connect and Login Method");
e.printStackTrace();
} catch (Exception e) {
Log.logError(TAG, "Error in Connect and Login Method");
e.printStackTrace();
}
}
Log.logInfo(TAG, "Inside getConnection - Returning connection");
return this.connection;
}
l = System.currentTimeMillis();
try{
https://riptutorial.com/ 1481
connect.login(user, pass);
}catch (Exception e){
Log.logError(TAG, "Issue in login, check the stacktrace");
e.printStackTrace();
}
ChatManager.getInstanceFor(connectAndLogin(context)).removeChatListener(chatManagerListener);
}
https://riptutorial.com/ 1482
//sdm.addFeature("jabber:iq:privacy");
sdm.addFeature("jabber.org/protocol/si");
sdm.addFeature("http://jabber.org/protocol/si");
sdm.addFeature("http://jabber.org/protocol/disco#info");
sdm.addFeature("jabber:iq:privacy");
public Chat createChat(Context context, EntityJid jid, String party1, String party2,
ChatMessageListener messageListener){
Chat chat = ChatManager.getInstanceFor(
XMPP.getInstance().connectAndLogin(context))
.createChat(jid, party1 + "-" + party2,
messageListener);
return chat;
}
https://riptutorial.com/ 1483
} catch (SmackException.NoResponseException e) {
e.printStackTrace();
} catch (SmackException.NotConnectedException e) {
e.printStackTrace();
}
return false;
}
try {
return true;
} catch (Exception e) {
e.printStackTrace();
try {
XMPP.getInstance()
.login(user, pass, username);
sendBroadcast(new Intent("liveapp.loggedin"));
return true;
} catch (XMPPException e1) {
e1.printStackTrace();
} catch (SmackException e1) {
e1.printStackTrace();
} catch (InterruptedException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}catch (Exception e1){
e1.printStackTrace();
}
}
return false;
}
public UserLoginTask() {
}
if (register(mEmail, mPassword)) {
try {
XMPP.getInstance().close();
} catch (Exception e) {
e.printStackTrace();
}
}
return login(mEmail, mPassword, mUsername);
https://riptutorial.com/ 1484
protected void onCancelled() {
mAuthTask = null;
@Override
protected void onPreExecute() {
super.onPreExecute();
}
};
@Override
public void chatCreated(Chat chatCreated, boolean local) {
onChatCreated(chatCreated);
}
};
try {
String opt_jidStr = "abc";
try {
opt_jid = JidCreate.bareFrom(Localpart.from(opt_jidStr), Domainpart.from(HOST));
} catch (XmppStringprepException e) {
e.printStackTrace();
}
String addr1 = XMPP.getInstance().getUserLocalPart(getActivity());
String addr2 = opt_jid.toString();
if (addr1.compareTo(addr2) > 0) {
String addr3 = addr2;
addr2 = addr1;
https://riptutorial.com/ 1485
addr1 = addr3;
}
chat = XMPP.getInstance().getThreadChat(getActivity(), addr1, addr2);
if (chat == null) {
chat = XMPP.getInstance().createChat(getActivity(), (EntityJid) opt_jid, addr1,
addr2, messageListener);
PurplkiteLogs.logInfo(TAG, "chat value single chat 1 :" + chat);
} else {
chat.addMessageListener(messageListener);
PurplkiteLogs.logInfo(TAG, "chat value single chat 2:" + chat);
}
} catch (Exception e) {
e.printStackTrace();
}
XMPP.getInstance().addStanzaListener(getActivity(), packetListener);
XMPP.getInstance().addChatListener(getActivity(), chatListener);
XMPP.getInstance().getSrvDeliveryManager(getActivity());
} else {
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* user attemptLogin for xmpp
*
*/
if (cancel) {
focusView.requestFocus();
} else {
try {
mAuthTask = new UserLoginTask();
mAuthTask.execute((Void) null);
} catch (Exception e) {
}
}
https://riptutorial.com/ 1486
chat.removeMessageListener(messageListener);
chat = chatCreated;
chat.addMessageListener(messageListener);
}
} else {
chat = chatCreated;
chat.addMessageListener(messageListener);
}
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
try {
XMPP.getInstance().removeChatListener(getActivity(), chatListener);
if (chat != null && messageListener != null) {
XMPP.getInstance().removeStanzaListener(getActivity(), packetListener);
chat.removeMessageListener(messageListener);
}
} catch (Exception e) {
e.printStackTrace();
}
https://riptutorial.com/ 1487
Chapter 266: Xposed
Examples
Creating a Xposed Module
Xposed is a framework that allows you to hook method calls of other apps. When you do a
modification by decompiling an APK, you can insert/change commands directly wherever
you want. However, you will need to recompile/sign the APK afterwards and you can only
distribute the whole package. With Xposed, you can inject your own code before or after
methods, or replace whole methods completely. Unfortunately, you can only install Xposed
on rooted devices. You should use Xposed whenever you want to manipulate the behavior
of other apps or the core Android system and don't want to go through the hassle of
decompiling, recompiling and signing APKs.
repositories {
jcenter();
}
provided 'de.robv.android.xposed:api:82'
provided 'de.robv.android.xposed:api:82:sources'
Now you have to place these tags inside the application tag found in the AndroidManifest.xml so
Xposed recognizes your module:
<meta-data
android:name="xposedmodule"
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="YOUR_MODULE_DESCRIPTION" />
<meta-data
android:name="xposedminversion"
android:value="82" />
Hooking a method
https://riptutorial.com/ 1488
public class MultiPatcher implements IXposedHookLoadPackage
{
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws
Throwable
{
}
}
Inside the method, you check loadPackageParam.packageName for the package name of the app you
want to hook:
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws
Throwable
{
if (!loadPackageParam.packageName.equals("other.package.name"))
{
return;
}
}
Now you can hook your method and either manipulate it before it's code is run, or after:
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws
Throwable
{
if (!loadPackageParam.packageName.equals("other.package.name"))
{
return;
}
XposedHelpers.findAndHookMethod(
"other.package.name",
loadPackageParam.classLoader,
"otherMethodName",
YourFirstParameter.class,
YourSecondParameter.class,
new XC_MethodHook()
{
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable
{
Object[] args = param.args;
args[0] = true;
args[1] = "example string";
args[2] = 1;
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable
https://riptutorial.com/ 1489
{
Object result = param.getResult();
https://riptutorial.com/ 1490
Chapter 267: Youtube-API
Remarks
1. First of all, you need to download the latest jar from below link
https://developers.google.com/youtube/android/player/downloads/
2. You need to include this jar in your project. Copy and paste this jar in libs folder and don't
forget to add it in gradle file dependencies { compile files('libs/YouTubeAndroidPlayerApi.jar')
}
3. You need an api key to access youtube api's. Follow this link :
https://developers.google.com/youtube/android/player/register to generate your api key.
4. Clean and Build your project. Now you're ready to use the YoutubeAndroidPlayerApi For
playing an youtube video, you need to have video id corresponding to it so that you can play
it on youtube. For e.g : https://www.youtube.com/watch?v=B08iLAtS3AQ, B08iLAtS3AQ is
video id which you need to play it on youtube.
Examples
Launching StandAlonePlayerActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mApiKey = Config.YOUTUBE_API_KEY;
mPlayerView = new YouTubePlayerView(this);
mPlayerView.initialize(mApiKey, this); // setting up OnInitializedListener
addContentView(mPlayerView, new LayoutParams(LayoutParams.MATCH_PARENT,
https://riptutorial.com/ 1491
LayoutParams.MATCH_PARENT)); //show it in full screen
}
@Override
public void onInitializationFailure(YouTubePlayer.Provider provider,
YouTubeInitializationResult errorReason) {
@Override
public void onAdStarted() {
}
@Override
public void onLoaded(String videoId) { //video has been loaded
if(!TextUtils.isEmpty(mVideoId) && !this.isFinishing() && mYouTubePlayer != null)
mYouTubePlayer.play(); // if we dont call play then video will not auto play, but
user still has the option to play via play button
}
@Override
public void onLoading() {
}
@Override
public void onVideoEnded() {
}
@Override
public void onVideoStarted() {
@Override
public void onError(ErrorReason reason) {
Log.e("onError", "onError : " + reason.name());
}
The following code implements a simple YoutubePlayerFragment. The activity's layout is locked in
portrait mode and when orientation changes or the user clicks full screen at the YoutubePlayer it
turns to lansscape with the YoutubePlayer filling the screen. The YoutubePlayerFragment does
https://riptutorial.com/ 1492
not need to extend an activity provided by the Youtube library. It needs to implement
YouTubePlayer.OnInitializedListener in order to get the YoutubePlayer initialized. So our Activity's
class is the following
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;
import com.google.android.youtube.player.YouTubeInitializationResult;
import com.google.android.youtube.player.YouTubePlayer;
import com.google.android.youtube.player.YouTubePlayerFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
youTubePlayerFragment.initialize(API_KEY, this);
/**
*
* @param provider The provider which was used to initialize the YouTubePlayer
* @param youTubePlayer A YouTubePlayer which can be used to control video playback in the
provider.
* @param wasRestored Whether the player was restored from a previously saved state, as
part of the YouTubePlayerView
* or YouTubePlayerFragment restoring its state. true usually means
playback is resuming from where
* the user expects it would, and that a new video should not be loaded
*/
@Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer
youTubePlayer, boolean wasRestored) {
youTubePlayer.setFullscreenControlFlags(YouTubePlayer.FULLSCREEN_FLAG_CONTROL_ORIENTATION |
YouTubePlayer.FULLSCREEN_FLAG_ALWAYS_FULLSCREEN_IN_LANDSCAPE);
if(!wasRestored) {
youTubePlayer.cueVideo(VIDEO_ID);
}
}
/**
*
* @param provider The provider which failed to initialize a YouTubePlayer.
* @param error The reason for this failure, along with potential resolutions to this
https://riptutorial.com/ 1493
failure.
*/
@Override
public void onInitializationFailure(YouTubePlayer.Provider provider,
YouTubeInitializationResult error) {
if(error.isUserRecoverableError()) {
error.getErrorDialog(this,REQUEST_CODE).show();
} else {
String errorMessage = String.format("There was an error initializing the
YoutubePlayer (%1$s)", error.toString());
Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show();
}
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<fragment
android:id="@+id/youtubeplayerfragment"
android:name="com.google.android.youtube.player.YouTubePlayerFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:text="This is a YoutubePlayerFragment example"
android:textStyle="bold"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
https://riptutorial.com/ 1494
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:text="This is a YoutubePlayerFragment example"
android:textStyle="bold"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:text="This is a YoutubePlayerFragment example"
android:textStyle="bold"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:text="This is a YoutubePlayerFragment example"
android:textStyle="bold"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:text="This is a YoutubePlayerFragment example"
android:textStyle="bold"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:text="This is a YoutubePlayerFragment example"
android:textStyle="bold"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:text="This is a YoutubePlayerFragment example"
android:textStyle="bold"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
Lastly you need to add the following attributes in your Manifest file inside the activity's tag
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="portrait"
First you'll need to get the SHA-1 fingerprint on your machine using java keytool. Execute the
https://riptutorial.com/ 1495
below command in cmd/terminal to get the SHA-1 fingerprint.
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
@Override
public void onInitializationFailure(YouTubePlayer.Provider provider,
YouTubeInitializationResult errorReason) {
if (errorReason.isUserRecoverableError()) {
errorReason.getErrorDialog(this, RECOVERY_DIALOG_REQUEST).show();
} else {
String errorMessage = String.format(
getString(R.string.error_player), errorReason.toString());
Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show();
}
}
@Override
public void onInitializationSuccess(YouTubePlayer.Provider provider,
YouTubePlayer player, boolean wasRestored) {
if (!wasRestored) {
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
https://riptutorial.com/ 1496
if (requestCode == RECOVERY_DIALOG_REQUEST) {
// Retry initialization if user performed a recovery action
getYouTubePlayerProvider().initialize(Config.DEVELOPER_KEY, this);
}
}
Now create Config.java file. This file holds the Google Console API developer key and YouTube
video id
Config.java
// Developer key
public static final String DEVELOPER_KEY = "AIzaSyDZtE10od_hXM5aXYEh6Zn7c6brV9ZjKuk";
// YouTube video id
public static final String YOUTUBE_VIDEO_CODE = "_oEA18Y8gM0";
}
xml file
<com.google.android.youtube.player.YouTubePlayerView
android:id="@+id/youtube_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp" />
This example will guide you how to get playlist data using the YouTube Data API on Android.
SHA-1 fingerprint
First you need to get an SHA-1 fingerprint for your machine. There are various methods for
retrieving it. You can choose any method provided in this Q&A.
Now that you have an SHA-1 fingerprint, open the Google API console and create a project. Go to
this page and create a project using that SHA-1 key and enable the YouTube Data API. Now you
will get a key. This key will be used to send requests from Android and fetch data.
Gradle part
You will have to add the following lines to your Gradle file for the YouTube Data API:
compile 'com.google.apis:google-api-services-youtube:v3-rev183-1.22.0'
https://riptutorial.com/ 1497
In order to use YouTube's native client to send requests, we have to add the following lines in
Gradle:
compile 'com.google.http-client:google-http-client-android:+'
compile 'com.google.api-client:google-api-client-android:+'
compile 'com.google.api-client:google-api-client-gson:+'
The following configuration also needs to be added in Gradle in order to avoid conflicts:
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.2'
}
build.gradle
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.google.apis:google-api-services-youtube:v3-rev183-1.22.0'
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:support-v4:25.3.1'
compile 'com.google.http-client:google-http-client-android:+'
compile 'com.google.api-client:google-api-client-android:+'
compile 'com.google.api-client:google-api-client-gson:+'
}
Now comes the Java part. Since we will be using HttpTransport for networking and GsonFactory for
converting JSON into POJO, we don't need any other library to send any requests.
https://riptutorial.com/ 1498
Now I want to show how to get playlists via the YouTube API by providing the playlist IDs. For this
task I will use AsyncTask. To understand how we request parameters and to understand the flow,
please take a look at the YouTube Data API.
@Override
protected PlaylistListResponse doInBackground(String[]... params) {
PlaylistListResponse playlistListResponse;
try {
playlistListResponse = mYouTubeDataApi.playlists()
.list(YOUTUBE_PLAYLIST_PART)
.setId(TextUtils.join(",", playlistIds))
.setFields(YOUTUBE_PLAYLIST_FIELDS)
.setKey(AppConstants.YOUTUBE_KEY) //Here you will have to provide the keys
.execute();
} catch (IOException e) {
e.printStackTrace();
return null;
}
return playlistListResponse;
}
}
The above asynchronous task will return an instance of PlaylistListResponse which is a build-in
class of the YouTube SDK. It has all the required fields, so we don't have to create POJOs ourself.
@Override
protected void onPreExecute() {
https://riptutorial.com/ 1499
progressDialog.setTitle("Please wait.....");
progressDialog.show();
super.onPreExecute();
}
@Override
protected void onPostExecute(PlaylistListResponse playlistListResponse) {
super.onPostExecute(playlistListResponse);
//Here we get the playlist data
progressDialog.dismiss();
Log.d(TAG, playlistListResponse.toString());
}
}.execute(ids);
}
}
https://riptutorial.com/ 1500
Chapter 268: Zip file in android
Examples
Zip file on android
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
out.close();
} catch(Exception e) {
e.printStackTrace();
}
https://riptutorial.com/ 1501
Read Zip file in android online: https://riptutorial.com/android/topic/8137/zip-file-in-android
https://riptutorial.com/ 1502
Credits
S.
Chapters Contributors
No
Accessing SQLite
3 databases using the Adil Saiyad, emecas, honk
ContentValues class
Accounts and
4 gaara87, systemovich
AccountManager
https://riptutorial.com/ 1503
Bridge) AndroidMechanic, Anirudh Sharma, Anup Kulkarni, auval,
Barend, Blackbelt, Burak Day, Charuක, Chris Stratton, Da-
Jin C, Dale, Daniel Nugent, David Cheung, Erik, Fabio,
fyfyone Google, g4s8, Gabriele Mariotti, grebulon,
Hannoun Yassir, Hi I'm Frogatto, hichris123, honk, jim,
Kashyap Jha, Laurel, MCeley, Menasheh, Natali, Nemus,
Pavel Durov, Piyush, R. Zagórski, RishbhSharma, stkent,
Sudip Bhandari, sukumar, theFunkyEngineer, thiagolr, Tien
, Xaver Kapeller, Yassie, younes zeboudj, Yury Fedorov
Adding a FuseView to an
10 Tudor Luca
Android Project
12 AIDL Krishnakanth
Android Architecture
14 DeKaNszn
Components
Android game
16 Zoe
development
Android Kernel
18 honk, Sneh Pandya
Optimization
https://riptutorial.com/ 1504
Media
Animated AlertDialog
29 krunal patel, Thomas Easo
Box
https://riptutorial.com/ 1505
38 Bitmap Cache Lokesh Desai
Bluetooth and Bluetooth antonio, Jon Adams, Lukas, Myon, Pavel Durov, R.
39
LE API Zagórski, Reaz Murshed, V-PTR, WMios
Building Backwards
44 Jon Adams, mnoronha, RamenChef, SoroushA
Compatible Apps
https://riptutorial.com/ 1506
Carlos, Dan, Er. Kaushik Kajavadara, Gabriele Mariotti,
Kaushik, Nougat Lover, RamenChef, S.R, Sneh Pandya,
52 CardView
Somesh Kumar, Stephen Leppik, sud007, WarrenFaith,
Yury Fedorov
Convert vietnamese
61 string to english string 1SStorm
Android
https://riptutorial.com/ 1507
Creating Overlay honk, mnoronha, NitZRobotKoder, Rupali, Sujith
68
(always-on-top) Windows Niraikulathan
Data
74 honk, HoseinIT, Robert
Encryption/Decryption
Data Synchronization
75 Arpit Gandhi, mnoronha
with Sync Adapter
DayNight Theme
77 (AppCompat v23.2 / API Ishita Sinha
14+)
https://riptutorial.com/ 1508
Bearden, JJ86, Lewis McGeary, M D P, Mochamad Taufik
Hidayat, Rajesh, RamenChef, Ravi Rupareliya, RediOne1,
Sanket Berde, ShivBuyya, Yojimbo, Zoe
Enhancing Android
90 Performance Using Icon Beto Caldas, honk, Neeraj
Fonts
92 ExoPlayer Hamed Gh
95 Fastjson KeLiuyue
https://riptutorial.com/ 1509
Fingerprint API in
99 Doron Yakovlev-Golani, RamenChef, user01232
android
Formatting phone
106 Pedro Varela
numbers with pattern.
https://riptutorial.com/ 1510
Getting system font
114 names and using the Adil Saiyad, honk
fonts
https://riptutorial.com/ 1511
Bhatt, Sneh Pandya, Tim Kranen, Vincent D., Yury
Fedorov
Hardware Button
128 Events/Intents (PTT, JensV
LWP, etc.)
136 Installing apps with ADB Ahmad Aghazadeh, fyfyone Google, Laurel, Xaver Kapeller
https://riptutorial.com/ 1512
antonio, Apoorv Parmar, auval, Avinash R, Bartek Lipinski,
Blundering Philosopher, bpoiss, cascal, Charuක, Clinton
Yeboah, Code.IT, Cold Fire, dakshbhatt21, Dalija Prasnikar
, Daniel Käfer, Daniel Nugent, Daniel Stradowski,
DanielDiSu, Dave Thomas, David G., Devid Farinelli,
devnull69, DoNot, DVarga, Eixx, EKN, Erik Minarini, faranjit
, Floern, fracz, Franck Dernoncourt, g4s8, Gabriele Mariotti
, GingerHead, granmirupa, Harish Gyanani, Hi I'm Frogatto
, Ibrahim, iliketocode, insomniac, Irfan, Irfan Raza, Ironman
, Ivan Wooll, Jarrod Dixon, jasonlam604, Jean Vitor,
jhoanna, JSON C11, Justcurious, kann, Karan Nagpal,
Kayvan N, Lee, leodev, Lewis McGeary, MalhotraUrmil,
Mark Ormesher, MathaN, Mauker, Max, mnoronha, Mr.
Sajid Shaikh, Muhammed Refaat, muratgu, N J, Nick
Cardoso, niknetniko, noɥʇʎԀʎzɐɹƆ, Oren, Paresh Mayani,
Parsania Hardik, Paul Lammertsma, Pavneet_Singh,
penkzhou, Peter Mortensen, Phan Van Linh, Piyush, R.
Zagórski, Radouane ROUFID, Rajesh, RamenChef, rap-2-
h, rciovati, Reaz Murshed, RediOne1, rekire, reVerse,
russjr08, Ryan Hilbert, sabadow, Saveen, Simon, Simplans
, SoroushA, spaceplane, Stelian Matei, Stephane Mathis,
Stephen Leppik, sukumar, tainy, theFunkyEngineer,
ThomasThiebaud, ʇolɐǝz ǝɥʇ qoq, Tyler Sebastian,
vasili111, Vasily Kabunov, Vinay , Vivek Mishra, Xaver
Kapeller, younes zeboudj, Yury Fedorov, Zoe
Internationalization and
144 localization (I18N and Ankur Aggarwal
L10N)
https://riptutorial.com/ 1513
Abhishek Jain, AndroidMechanic,
AndroidRuntimeException, baozi, Ben Trengrove, cdeange
, Daniel Nugent, Diti, Eliezer, Endzeit, Florent Spahiu,
Gabriele Mariotti, ganesshkumar, gerard, Graham Smith,
JSON in Android with harsh_v, Ic2h, IncrediApp, johnrao07, Kaushik, L. Swifter,
150
org.json Linda, Luca Faggianelli, Mannaz, Mauker, Michael Spitsin,
Monish Kamble, Muhammed Refaat, N J, Oleksandr,
Parsania Hardik, Prownage, rekire, Siddhesh, StuStirling,
ThomasThiebaud, Tim Kranen, user01232, Vincent D.,
Xaver Kapeller, younes zeboudj, Yury Fedorov
Library Dagger 2:
154 Dependency Injection in Er. Kaushik Kajavadara, honk
Applications
Loading Bitmaps
158 iDevRoids
Effectively
Localized Date/Time in
160 Geert, honk, mnoronha
Android
https://riptutorial.com/ 1514
Alex Chengalan, Aryan, BadCash, Daniel Nugent, Hiren
161 Location Patel, Mahmoud Ibrahim, MidasLefko, Pablo Baxter,
RamenChef, Stephen Leppik
https://riptutorial.com/ 1515
Bhargavi Yamanuri, Chip, Daniel Nugent, Hi I'm Frogatto,
170 Menu
honk, Iman Hamidi
Multidex and the Dex Adarsh Ashok, Ben, bigbaldy, cdeange, Daniel Nugent,
172
Method Limit Gabriele Mariotti, Mike, Pongpat, R. Zagórski, Shirane85
Notification Channel
176 Lokesh Desai
Android O
Pagination in
185 Muhammad Younas
RecyclerView
Performance
188 honk, Jonas Köritz
Optimization
https://riptutorial.com/ 1516
Mariotti, Ichthyocentaurs, LoungeKatt, Milad Nouri,
once2go, oshurmamadov, Piyush, pRaNaY, Pro Mode,
RamenChef, Rucha Bhatt, Sanket Berde, Shinil M S,
Ufkoku, VISHWANATH N P, vrbsm, y.feizi
Publish a library to
197 Farid
Maven Repositories
https://riptutorial.com/ 1517
Zilk
https://riptutorial.com/ 1518
SharedPreferences
Shared Element
216 noongiya95
Transitions
Speech to Text
223 Hitesh Sahu, honk, RamenChef, Stephen Leppik
Conversion
https://riptutorial.com/ 1519
Screen Activities
Storing Files in Internal & Amit Vaghela, Andrew Brooke, AnV, Daniel Nugent,
227
External Storage Gabriele Mariotti, Nickan B, Uttam Panchasara
SyncAdapter with
231 periodically do sync of Bhargavi Yamanuri
data
238 The Manifest File John Snow, Jon Adams, kit, mayojava, Menasheh
https://riptutorial.com/ 1520
Jason Robinson, Laurel, noob, Piyush, R. Zagórski,
RamenChef, Tot Zam, Vlonjat Gashi
Typedef Annotations:
247 Gabriele Mariotti, hardik m, mmBs, Pongpat
@IntDef, @StringDef
250 Universal Image Loader Greg T, honk, Jon Adams, priyankvex, Stephen Leppik
https://riptutorial.com/ 1521
Mariotti, Moustachauve, RamenChef, RediOne1, Rucha
Bhatt, Sneh Pandya, Stephen Leppik, Usman, ZeroOne
What is ProGuard? What Ayush Bansal, Daniel Nugent, Ghanshyam Sharma, Pratik
260
is use in Android? Butani
https://riptutorial.com/ 1522