This lesson:
- explore Uri and data intent attribute;
- launch system apps (browser, dialer, map).
We know that Intent has an action attribute. It is used to indicate action: viewing or editing for example. It’s common, that action is often applied to smth,so we need to declare an object, but not only an action, and Intent has data attribute for it.
A way to set value to this attribute is Intent’s setData (Uri data) method, and Uri object must be passed to this method.
Uri is an object which uses string, disassembles it to parts and remember it. We are not allowed to use any string we want, we can only use strings which are declared according to this document: RFC 2396. Uri has lot’s of methods which allow us to retrieve separate parts from parsed string.
I will create Uri from string and pass (using colon) method’s name and a value which it returns to logs. Let’s take this string, http address for example:
Uri uri = Uri.parse("http://developer.android.com/reference/android/net/Uri.html");
Let’s look at what methods return:
uri.getScheme(): http
uri.getSchemeSpecificPart(): //developer.android.com/reference/android/net/Uri.html
uri.getAuthority(): developer.android.com
uri.getHost(): developer.android.com
uri.getPath(): /reference/android/net/Uri.html
uri.getLastPathSegment(): Uri.html
Scheme, Authority, Host, Path, and others are concepts, taken from RFC document I linked before. Following the link you can find full description of them, understand their meaning and verify with what was returned by Uri.
Let’s take a look at some more example:
FTP
Uri uri = Uri.parse("ftp://bob @ google.com:80/data/files");
(As for real, the code above is written in one string. There are spaces before and after @ because of layout features)
uri.getScheme(): ftp
uri.getSchemeSpecificPart(): //This email address is being protected from spambots. You need JavaScript enabled to view it.:80/data/files
uri.getAuthority(): This email address is being protected from spambots. You need JavaScript enabled to view it.:80
uri.getHost(): google.com
uri.getPort(): 80
uri.getPath(): /data/files
uri.getLastPathSegment(): files
uri.getUserInfo(): bob
Coordinates
Uri uri = Uri.parse("geo:55.754283,37.62002");
uri.getScheme(): geo
uri.getSchemeSpecificPart(): 55.754283,37.62002
We only could retrieve Scheme and SchemeSpecificPart here.
Phone number
Uri uri = Uri.parse("tel:12345");
uri.getScheme(): tel
uri.getSchemeSpecificPart():12345
Similarly, only two parts we could retrieve here.
Contact
Uri uri = Uri.parse("content://contacts/people/1");
uri.getScheme(): content
uri.getSchemeSpecificPart(): //contacts/people/1
uri.getAuthority(): contacts
uri.getPath(): /people/1
uri.getLastPathSegment(): 1
Scheme equals to content within this example. It’s a special data type - Content Provider which allows any program to give access to it’s data, and other programs to read and edit this data. We will unveil this topic later and create this data type by ourselves.
You can inquire standard supported Uris here
Examples show that Uri could be created from completely different strings: http address, ftp address, coordinates, phone numbers, contacts.
Content type could be identified by Scheme. Also, Scheme could be adjusted within Intent Filter, and we can filter Intents with Uri’s data type we need, only http for example. We will do it later, and now let’s code a simple example, where we will declare an Intent with action and data, send it and wait for result. Let’s view http address, coordinates on a map and open the dialer.
We need Google Maps App to view map coordinates. There is no such an app in standard android system images (that you downloaded with SDK Manager). We need system image which name begins with "Google APIs".
Create AVD with platform Google APIs with API Level 10. Call it any way you want
Let’s create a project. Note, that we use Google APIs 2.3.3
Project name: P0311_SimpleIntents
Build Target: Google APIs 2.3.3
Application name: SimpleIntents
Package name: ru.startandroid.develop.p0311simpleintents
Create Activity: MainActivity
(If you didn’t manage to load Google APIs, just create a new project as usual with Android 2.3.3 platform. Google Maps just won’t work within this example).
Let’s code main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal"> <Button android:id="@+id/btnWeb" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:layout_weight="1" android:text="Web"> </Button> <Button android:id="@+id/btnMap" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:layout_weight="1" android:text="Map"> </Button> <Button android:id="@+id/btnCall" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:layout_weight="1" android:text="Call"> </Button> </LinearLayout>
There are three buttons on the screen: the first one will open a web-page, the second is for map opening and the third is for dialer.
Let's code MainActivity.java:
package ru.startandroid.develop.p0311simpleintents; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity implements OnClickListener { Button btnWeb; Button btnMap; Button btnCall; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btnWeb = (Button) findViewById(R.id.btnWeb); btnMap = (Button) findViewById(R.id.btnMap); btnCall = (Button) findViewById(R.id.btnCall); btnWeb.setOnClickListener(this); btnMap.setOnClickListener(this); btnCall.setOnClickListener(this); } @Override public void onClick(View v) { Intent intent; switch (v.getId()) { case R.id.btnWeb: intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://developer.android.com")); startActivity(intent); break; case R.id.btnMap: intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.setData(Uri.parse("geo:55.754283,37.62002")); startActivity(intent); break; case R.id.btnCall: intent = new Intent(Intent.ACTION_DIAL); intent.setData(Uri.parse("tel:12345")); startActivity(intent); break; } } }
I followed three different ways to create an Intent with attributes.
I used Intent (String action, Uri uri) constructor for btnWeb. It creates an intent with action and data variables. We use a standard action – ACTION_VIEW, it indicates if we want to view something. We pass Uri, created from web link http://developer.android.com, as data variable. If we try to describe in words our code, we will get something like this: our intent indicates if we want to view this link’s content and looking for activity to do it.
In case with btnMap I used Intent() constructor. It just creates intent, and we determine action and data attributes for it within next rows. action is ACTION_VIEW again and we use Uri was which created from coordinates - 55.754283,37.62002, as data. This intent indicates that we want to see the coordinates on a map.
We use Intent (String action) constructor for btnCall. It needs an action to be set at once, but data attribute will be determined later. action (in this case it is ACTION_DIAL ) opens dialer and dial a number determined in data, but it doesn’t launch a call. We set Uri, created from phone number 12345, as data.
All this three ways lead us to same result: we will get an intent with determined action and data attributes. You decide which one to use, depending on the situation.
We need an internet connection to be on our PC, because App will use it to open the link and show the map.
Add a Users Permission element within app’s manifest and choose android.permission.INTERNET for it’s name. It will allow internet connection to the app. In my case it works even without this permission, but I can’t get why…
Let’s save and launch the app.
Click Web button.
Standard browser will be launched, showing our link content.
Return to the main screen and click Map.
Map which shows place, according to determined coordinates, is now displayed.
Return to the main screen and click Call.
Standard dialer is displayed and we see that determined within data number is already dialed. We just need to click the call-button.
Most likely, you may have lot’s of "what if" questions now. I can answer some of them at once and suggest you to make some experiments within this app.
1) What if we don’t use "geo" prefix:
System will warn you, that there is no suitable activity, because Map Activity is adjusted to work with Schema = geo, I think.
If we don’t use tel prefix, Call Activity similarly won’t work.
2) What if we use geo with out of range coordinates:
If we use, out of range coordinates for example, like geo:a,b, map Activity will be launched, but will warn: "Unable to load the URL". I.e. data was suitable by Schema, but was incorrect.
3) What if we use wright coordinates, but the action will be ACTION_EDIT instead of ACTION_VIEW.
It turns out that we want to edit place on map, defined with this coordinates. System will warn us, that there is no appropriate activity. Google app is waiting for Intent with action = ACTION_VIEW. It will show us place on a map, but it didn’t agree to edit this place )
It’s necessary to understand, that all system apps are designed for specific actions with specific data. So, if you try to make a call to http link or find a phone number on a map, system just won’t find appropriate activity.
Next lesson:
- code simple browser
Присоединяйтесь к нам в Telegram:
- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня