Default functions

On Android, there are certain services or features that work automatically as soon as the application is compiled with the latest Android SDK. However, this does not mean there is no work involved. In many cases it’s recommended—or even required—to participate in order to ensure proper behavior.

Adaptive icon

With the introduction of Android 8.0 (Oreo), adaptive icon support also arrived. This means that if the Home Screen app (Launcher app) supports it, the user can change the shape of app launcher icons. To make sure this looks good, a bit of extra work is required. In an Android app, you need to define the background and foreground images separately while keeping a certain safe zone. When the icon is masked during shape changes, only the background is clipped. If we ignore this, the build process will simply use a white background and place the old icon as the foreground image—which often doesn’t look good.

Official documentation:
https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive

Related English blog posts:
Understanding Android Adaptive Icons
Designing Adaptive Icons
Implementing Adaptive Icons

Runtime permission

With Android 6.0 (Marshmallow), the platform addressed one of its biggest shortcomings: permissions for accessing various resources are now requested at runtime. Users can decide on each permission individually and can also change their decision later.

Previously, all permissions had to be accepted at installation time or the app wouldn’t install. This “all or nothing” approach benefited developers because it guaranteed that all permissions were granted before any code requiring them could run (since that happens after installation). But it also had disadvantages—mainly enabling misuse and discouraging users from installing certain apps. A favorite example is an SMS Backup app that also asks for access to contacts and call logs, because it can export those too. If all I want is to back up SMS messages, why should I allow call log access? Users would simply avoid installing the app even if it could perform the intended job perfectly.

With runtime permissions, nothing has to be accepted during installation; instead, permissions are requested as needed during app usage. This requires development work, as handling runtime permissions cannot be implemented without modifying the code. Special cases must also be handled, such as:

  • The user denies the request
  • Multiple permissions are needed together but one or more are denied
  • The user repeatedly reaches a function and keeps denying the request, eventually making the denial permanent

platform_integration_-_run_time_permission.jpg

Above: the SmartCity app requesting permissions on Android 4.2 (left) and Android 8.0 (right).

The implementation of Runtime Permissions is now unavoidable. Until November 2018, apps built with older SDK versions (< API 23) still follow the old rules. To prevent developers from exploiting this and to encourage progress, Google announced that after November 2018 such apps cannot be published to the Play Store. For new apps, this rule applies as early as August 2018.

More info on the official Android developer blog:
https://android-developers.googleblog.com/2017/12/improving-app-security-and-performance.html

Useful helper library:
PermissionsDispatcher

Notification Channel

Android 8.0 (Oreo) introduced Notification Channels, which allow categorization of notifications on the client side. If the compile target is API 26 or higher, developers must create these channels, or notifications simply won’t appear on Oreo. Beyond becoming a requirement, the concept has real advantages.

During development, every notification must be assigned to a channel, for example:

// ... get your a notification manager instance by NotificationCompat as usual
// Set default channel on Android O and above: MANDATORY
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
	NotificationChannel channel = new NotificationChannel(
			NOTIFICATION_CHANNEL_ID,
			getString(R.string.application_name),
			NotificationManager.IMPORTANCE_HIGH
	);
	channel.enableLights(true);
	channel.setLightColor(getColor(R.color.dh_magenta));
	channel.enableVibration(true);
	channel.setLockscreenVisibility(NotificationCompat.VISIBILITY_SECRET);
	mNotificationManager.createNotificationChannel(channel);
}
// Rest of your code ...

Notice how channel names and IDs could even be sent from the server via push notifications (FCM, GCM). This allows adding new categories without updating the Play Store listing. For example, news apps could create channels per category dynamically. Users benefit as well, since they can mute or customize notifications on a per-channel basis in system settings—ending yet another “all or nothing” scenario.

Split screen

With Android 7.0 (Nougat), Google improved support for larger screens (tablets, TVs). The split screen feature allows two simultaneously active apps on the display. This slightly breaks the previous Android philosophy that only one app runs in the foreground at a time. This feature is automatically enabled when building with a modern SDK.

platform_integration_-_split_screen.jpg

SmartCity app in split-screen mode, featuring an active editing screen on the left and the website in Chrome on the right.

You should still prepare your app for various screen sizes. (Some vendors may even force split screen—e.g., Samsung, OnePlus.) Do not skip implementing landscape mode! Also fully test app lifecycle handling and state retention. Finally, declare which modes your app supports:

android:resizeableActivity=["true" | "false"]
android:supportsPictureInPicture=["true" | "false"]

Layouts can further refine dimensions:

<activity android:name=".MyActivity">
    <layout android:defaultHeight="500dp"
          android:defaultWidth="600dp"
          android:gravity="top|end"
          android:minHeight="450dp"
          android:minWidth="300dp" />
</activity>

There may be further tasks beyond this, so always test thoroughly!

Scoped Directory Access

Traditionally, apps could request write (WRITE_EXTERNAL_STORAGE) and read (READ_EXTERNAL_STORAGE) permission for external storage. This is another “all or nothing” scenario. Scoped Directory Access solves this by granting third-party apps access only to specific directories. For example, a photo viewer could only access downloaded files.

More info:
https://developer.android.com/training/articles/scoped-directory-access


Additional integration possibilities

Below are further integration options that do not come automatically—you must implement them yourself.

RTL support

Right-to-left (RTL) support refers to handling languages that read from right to left. On Android, this affects not only text but also layout element ordering. Once the concept is understood, implementation becomes easier. The main rule is to use start instead of left and end instead of right wherever possible. A few examples:

gravity="left" > gravity="start"
gravity="right" > gravity="end"
layout_gravity="left" > layout_gravity="start"
layout_gravity="right" > layout_gravity="end"

marginLeft > marginStart
marginRight > marginEnd
paddingLeft > paddingStart
paddingRight > paddingEnd
drawableLeft > drawableStart
drawableRight > drawableEnd

alignParentLeft > alignParentStart
alignParentRight > alignParentEnd
toLeftOf > toStartOf
toRightOf > toEndOf

When the app runs in an RTL environment, not only the text alignment is reversed—UI elements flip as well. For example, if a list item contains an icon followed by text in LTR mode, this becomes text followed by icon in RTL mode.

platform_integration_-_rtl.jpg

Screenshots of the SmartCity app in left-to-right and right-to-left display mode.

After this, text, icons, and animations may still need adjustments. Text direction is controlled by textDirection, not by gravity. Animations may also require changes if the motion direction conveys meaning. Icons that visually imply direction (e.g., back button arrows) also need mirroring.

Two approaches:

1. For raster images (PNG, JPEG), create an alternative directory, e.g., drawable-ldrtl, and place mirrored assets there.

2. For vector images (VectorDrawable), you can set android:autoMirrored="true" to auto-mirror the drawable.

Account Manager

Most apps act as clients while business logic runs server-side—bringing authentication with it. Developers often build custom solutions from scratch: custom UI, custom manager classes, custom authentication flows. Yet Android offers an AccountManager API to unify such processes.

Benefits include:

  • Automatic form filling via Autofill Framework
  • User-specific unique identifiers not tied to the device
  • Server synchronization using multiple scheduling options

More info:
Identify your users
Creating Sync Adapters

Security features

Android Keystore System

This protects the most important information—preventing unauthorized access and preventing extraction of keys during usage. Since Android 9.0 (Pie), hardware-backed encryption modules are also available.

More details:
https://developer.android.com/training/articles/keystore

Biometric Prompt

After Android 6.0 introduced fingerprint authentication, Android 9.0 (Pie) unified biometric authentication under the Biometric Prompt system dialog. This system-level service allows developers to benefit from multiple biometric types (e.g., iris scan) without modifying their code.

More details:
Better Biometrics in Android P
BiometricPrompt API

Deep link & App link

Apps can react to links instead of opening them in a browser. This helps bridge navigation between websites and native apps. Links can also carry parameters that uniquely identify content.

Apps must declare which types of links or content they can handle, using custom protocols or schemes such as:

myapp://userlist/user?id=12345

Using the link, the exact screen and content can be identified. Implementation details:
Deep Linking on Android

The downside of custom schemes is that while Android can interpret them, browsers on other platforms cannot. HTTP-based links are more versatile. But theoretically, any app could claim to handle e.g.: http://google.com. This leads to conflicts where users must choose an app. Android App Links—available since Android 6.0—solve this by requiring domain verification.

More info on verification:
Verify App Links

Firebase App Indexing

This feature integrates the app with Google Search. Users can launch specific screens directly from search results. It helps users reach in-app content more easily. (This is one reason why proper Deep Links and App Links are important.)

Some areas where App Indexing helps:

  • Appearing in search results
  • Direct install button when your home screen appears as a result
  • Autocomplete support
  • Google Assistant integration
  • More accurate targeted ads

Demo video:
https://www.youtube.com/watch?v=C35OSHlTNwA

Detailed guide:
https://firebase.google.com/docs/app-indexing/

Launcher services

The Home Screen (Launcher) app is not an inseparable part of the Android system—it is itself an Android app packaged as an .apk. Anyone can build one. Google provides defaults via the AOSP project, but later Google Now Launcher and Pixel Launcher introduced further features.

Below are features developers can leverage:

1) App Widget

A long-standing feature allowing developers to create small functional UI blocks in place of regular launcher icons. Examples include weather, music players, etc. Widgets have limitations to avoid heavy battery consumption—e.g., they refresh infrequently by default.

Implementation guide:
App Widgets Overview

2) App shortcut

Introduced with Android 7.1 (Nougat) and Pixel Launcher. Long-pressing an app icon displays a small menu with static or dynamically generated shortcuts. These can also be pinned as standalone launcher icons. For instance, Gmail allows composing a new email directly.

Implementation guide:
App Shortcuts

IME services

At first glance, one might think keyboards (Input Method Editors) need no app-specific handling—but several integration points can improve the user experience.

1) Input type

Correctly setting EditText attributes greatly improves usability. The inputType influences the on-screen keyboard layout. For example, inputType="number" displays a large, number-only keyboard, making numeric entry easier. If expecting an email address, keys like @ or .com may be added automatically.

platform_integration_-_keyboard_input_type.jpg

Reference illustrations from documentation:
Keyboard Input Styles

This applies equally to web frontend development: the correct type attribute on an HTML <input /> greatly enhances usability. See corresponding StackOverflow entry.

2) Keyboard navigation

During form entry, keyboard navigation becomes important—especially on Android TV, where the remote is essential. Options include ordering tab navigation using attributes:

android:nextFocusForward
android:nextFocusUp
android:nextFocusDown
android:nextFocusLeft
android:nextFocusRight

The Input Method Action can also be set using android:imeOptions, values documented here:
Ime Options

3) Auto-fill

Introduced in Android 8.0 (Oreo). Reduces typing time and errors. By specifying hints, apps can help the framework understand the meaning of fields:

android:autofillHints="password"
android:importantForAutofill="auto|no|yes|noExcludeDescendants|yesExcludeDescendants"

Custom UI components can also integrate with the Autofill Framework.

More info:
Autofill Framework

Launching third-party apps

The simplest form of platform integration is launching external apps. Taking a typical “Contact” or “About Us” screen as an example: instead of only displaying contact info, apps can directly open relevant external tools to improve user experience.

Using URL schemes

With the concepts of Deep Links and custom URL schemes already introduced, the following will be straightforward. Build a standards-compliant link and launch it:

  • Start email composition with predefined recipients and subject, RFC-2368:
    mailto:info@yourdomain.hu?subject=App feedback
  • Start phone call according to RFC-3966:
    tel:+36??/???-????
    Alternative schemes include:
    call:, callto:, sip:, sips:
  • Send SMS
    sms:+36??/???-????
  • Open maps / navigation (RFC-5870):
    geo:latitude,longitude?z=zoom
    geo:latitude,longitude?q=query
    geo:0,0?q=my+street+address
    geo:0,0?q=latitude,longitude(label)
  • Open Play Store listing:
    market://details?id=<package_name>
    http://play.google.com/store/apps/details?id=<package_name>

Using code

Slightly more work is required when expecting return values, ensuring backward compatibility, or providing fallbacks.

Start camera and return with an image:

public void startCamera() {
	Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
	startActivityForResult(cameraIntent, 1234);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 1234) {
          Bitmap image = (Bitmap) data.getExtras().get("data");
          ImageView imageview = (ImageView) findViewById(R.id.imageview); 
          imageview.setImageBitmap(image);
    }
}

Start gallery selection and return with selected image:

public void startGallery() {
	Intent galleryIntent = new Intent(
		Intent.ACTION_PICK,
		android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI
	);
	startActivityForResult(galleryIntent , 1234);
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
	if (requestCode == 1234) {
        if (null != data) {
            imageUri = data.getData();
            //Do whatever that you desire
        }
    }
}

View photo using default viewer:

public void showPhoto(File file){
    Intent galleryIntent = new Intent(Intent.ACTION_VIEW, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
	galleryIntent.setDataAndType(Uri.fromFile(file), "image/*");
	galleryIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
	startActivity(galleryIntent);
}

Add calendar event with backward compatibility:

public boolean openCalendarAdd(Context context, Date begin, Date end, String title, String description, String location, int availability) {
	if (context == null) return false;
	if (begin == null) return false;

	Intent intent;
	if (end == null) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(begin);
		cal.add(Calendar.MINUTE, 30);
		end = cal.getTime();
	}

	if (Build.VERSION.SDK_INT >= 14) {
		intent = new Intent(Intent.ACTION_INSERT)
				.setData(CalendarContract.Events.CONTENT_URI)
				.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, begin.getTime())
				.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end.getTime())
				.putExtra(CalendarContract.Events.TITLE, title)
				.putExtra(CalendarContract.Events.DESCRIPTION, description)
				.putExtra(CalendarContract.Events.EVENT_LOCATION, location)
				.putExtra(CalendarContract.Events.AVAILABILITY, availability)
				.putExtra(CalendarContract.Events.ALL_DAY, false);
		return startActivity(context, intent);
	} else {
		intent = new Intent(Intent.ACTION_EDIT);
		intent.setType("vnd.android.cursor.item/event");
		intent.putExtra("beginTime", begin.getTime());
		intent.putExtra("endTime", end.getTime());
		intent.putExtra("title", title);
		intent.putExtra("description", description);
		intent.putExtra("eventLocation", location);
		intent.putExtra("availability", availability);
		intent.putExtra("allDay", false);
	}
	return startActivity(context, intent);
}

Open YouTube video with the app if installed, otherwise in browser:

public boolean openYoutubeVideo(Context context, String videoID) {
	Intent youtubeIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("vnd.youtube:" + videoID));
	youtubeIntent.setClassName("com.google.android.youtube", "com.google.android.youtube.WatchActivity");
	youtubeIntent.putExtra("VIDEO_ID", videoID);
	if (!startActivity(context, youtubeIntent)) {
		Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://m.youtube.com/watch?v=" + videoID));
		return startActivity(context, intent);
	}
	return true;
}

Missing third-party app

Well-designed apps also prepare for the absence of third-party apps needed to handle certain content. For example, tablets may not support phone calls or SMS, meaning no compatible app exists. A more common example is downloading a PDF but having no PDF reader installed.

If the system cannot find an app to handle an Intent, it throws ActivityNotFoundException. You can catch it and notify the user. We prefer going one step further—providing solutions rather than errors. If no PDF reader is installed, we offer a direct link to search for one in the Play Store using:

http://play.google.com/store/search?q=pdf+reader&c=apps


Although this summary is not exhaustive, I hope it brought new perspectives to application feature planning. Google releases new features every year, so staying up-to-date is essential. Wear-supported devices (Android Wear) already emphasize deeper integration such as Voice Command. Android Auto is also becoming more common, introducing new requirements for applications. To deliver the best possible experience, we must understand the platform’s unique characteristics.