Showing posts with label App Components. Show all posts
Showing posts with label App Components. Show all posts

Sunday, June 3, 2012

Using DialogFragments

[This post is by David Chandler, Android Developer Advocate — Tim Bray]

Honeycomb introduced Fragments to support reusing portions of UI and logic across multiple activities in an app. In parallel, the showDialog / dismissDialog methods in Activity are being deprecated in favor of DialogFragments.



In this post, I�ll show how to use DialogFragments with the v4 support library (for backward compatibility on pre-Honeycomb devices) to show a simple edit dialog and return a result to the calling Activity using an interface.
For design guidelines around Dialogs, see the Android Design site.

The Layout

Here�s the layout for the dialog in a file named fragment_edit_name.xml.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/edit_name"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:orientation="vertical" >

<TextView
android:id="@+id/lbl_your_name" android:text="Your name"
android:layout_width="wrap_content" android:layout_height="wrap_content" />

<EditText
android:id="@+id/txt_your_name"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:inputType=�text�
android:imeOptions="actionDone" />
</LinearLayout>

Note the use of two optional attributes. In conjunction with android:inputType=�text�, android:imeOptions=�actionDone� configures the soft keyboard to show a Done key in place of the Enter key.

The Dialog Code

The dialog extends DialogFragment, and since we want backward compatibility, we�ll import it from the v4 support library. (To add the support library to an Eclipse project, right-click on the project and choose Android Tools | Add Support Library...).

import android.support.v4.app.DialogFragment;
// ...

public class EditNameDialog extends DialogFragment {

private EditText mEditText;

public EditNameDialog() {
// Empty constructor required for DialogFragment
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_edit_name, container);
mEditText = (EditText) view.findViewById(R.id.txt_your_name);
getDialog().setTitle("Hello");

return view;
}
}

The dialog extends DialogFragment and includes the required empty constructor. Fragments implement the onCreateView() method to actually load the view using the provided LayoutInflater.

Showing the Dialog

Now we need some code in our Activity to show the dialog. Here is a simple example that immediately shows the EditNameDialog to enter the user�s name. On completion, it shows a Toast with the entered text.

import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
// ...

public class FragmentDialogDemo extends FragmentActivity implements EditNameDialogListener {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
showEditDialog();
}

private void showEditDialog() {
FragmentManager fm = getSupportFragmentManager();
EditNameDialog editNameDialog = new EditNameDialog();
editNameDialog.show(fm, "fragment_edit_name");
}

@Override
public void onFinishEditDialog(String inputText) {
Toast.makeText(this, "Hi, " + inputText, Toast.LENGTH_SHORT).show();
}
}

There are a few things to notice here. First, because we�re using the support library for backward compatibility with the Fragment API, our Activity extends FragmentActivity from the support library. Because we�re using the support library, we call getSupportFragmentManager() instead of getFragmentManager().

After loading the initial view, the activity immediately shows the EditNameDialog by calling its show() method. This allows the DialogFragment to ensure that what is happening with the Dialog and Fragment states remains consistent. By default, the back button will dismiss the dialog without any additional code.

Using the Dialog

Next, let�s enhance EditNameDialog so it can return a result string to the Activity.

import android.support.v4.app.DialogFragment;
// ...
public class EditNameDialog extends DialogFragment implements OnEditorActionListener {

public interface EditNameDialogListener {
void onFinishEditDialog(String inputText);
}

private EditText mEditText;

public EditNameDialog() {
// Empty constructor required for DialogFragment
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_edit_name, container);
mEditText = (EditText) view.findViewById(R.id.txt_your_name);
getDialog().setTitle("Hello");

// Show soft keyboard automatically
mEditText.requestFocus();
getDialog().getWindow().setSoftInputMode(
LayoutParams.SOFT_INPUT_STATE_VISIBLE);
mEditText.setOnEditorActionListener(this);

return view;
}

@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (EditorInfo.IME_ACTION_DONE == actionId) {
// Return input text to activity
EditNameDialogListener activity = (EditNameDialogListener) getActivity();
activity.onFinishEditDialog(mEditText.getText().toString());
this.dismiss();
return true;
}
return false;
}
}

For user convenience, we programmatically focus on the EditText with mEditText.requestFocus(). Alternatively, we could have used the <requestFocus/> tag in the layout XML to do this; however, in some cases it�s preferable to request focus programmatically. For example, an OnFocusChangeListener added in the Fragment�s onCreateView() method won�t get called if you request focus in the layout XML.

If the user focuses on an EditText, the soft keyboard will automatically appear. In order to force this to happen with our programmatic focus, we call getDialog().getWindow().setSoftInputMode(). Note that many Window operations you might have done previously in a Dialog can still be done in a DialogFragment, but you have to call getDialog().getWindow() instead of just getWindow(). The resulting dialog is shown on both a handset and tablet (not to scale):

The onEditorAction() method handles the callback when the user presses the Done key. It gets invoked because we�ve set an OnEditorActionListener on the EditText. It calls back to the Activity to send the entered text. To do this, EditNameDialog declares an interface EditNameDialogListener that is implemented by the Activity. This enables the dialog to be reused by many Activities. To invoke the callback method onFinishEditDialog(), it obtains a reference to the Activity which launched the dialog by calling getActivity(), which all Fragments provide, and then casts it to the interface type. In MVC architecture, this is a common pattern for allowing a view to communicate with a controller.

We can dismiss the dialog one of two ways. Here we are calling dismiss() within the Dialog class itself. It could also be called from the Activity like the show() method.

Hopefully this sheds some more light on Fragments as they relate to Dialogs. You can find the sample code in this blog post on Google Code.

References for learning more about Fragments:

Monday, June 20, 2011

Things That Cannot Change

[This post is by Dianne Hackborn, whose fingerprints can be found all over the Android Application Framework — Tim Bray]

Sometimes a developer will make a change to an application that has surprising results when installed as an update to a previous version — shortcuts break, widgets disappear, or it can�t even be installed at all. There are certain parts of an application that are immutable once you publish it, and you can avoid surprises by understanding them.

Your package name and certificate

The most obvious and visible of these is the �manifest package name,� the unique name you give to your application in its AndroidManifest.xml. The name uses a Java-language-style naming convention, with Internet domain ownership helping to avoid name collisions. For example, since Google owns the domain �google.com�, the manifest package names of all of our applications should start with �com.google.� It’s important for developers to follow this convention in order to avoid conflicts with other developers.

Once you publish your application under its manifest package name, this is the unique identity of the application forever more. Switching to a different name results in an entirely new application, one that can’t be installed as an update to the existing application.

Just as important as the manifest package name is the certificate that application is signed with. The signing certificate represents the author of the application. If you change the certificate an application is signed with, it is now a different application because it comes from a different author. This different application can�t be uploaded to Market as an update to the original application, nor can it be installed onto a device as an update.

The exact behavior the user sees when installing an application that has changed in one of these two ways is different:

  • If the manifest package name has changed, the new application will be installed alongside the old application, so they both co-exist on the user�s device at the same time.

  • If the signing certificate changes, trying to install the new application on to the device will fail until the old version is uninstalled.

If you change the signing certificate of your application, you should always change its manifest package name as well to avoid failures when it’s installed. In other words, the application coming from a different author makes it a different application, and its package name should be changed appropriately to reflect that. (Of course it’s fine to use the same package name for the development builds of your app signed with your test keys, because these are not published.)

Your AndroidManifest.xml is a public API

More than just your package name that is immutable. A major function of the AndroidManifest.xml is essentially to declare a public API from your application for use by other applications and the Android system. Every component you declare in the manifest that is not private (that is whose android:exported state is true) should be treated as a public API and never changed in a way that breaks compatibility.

A subtle but important aspect of what constitutes a break in compatibility is the android:name attribute of your activity, service, and receiver components. This can be surprising because we think of android:name as pointing to the private code implementing our application, but it is also (in combination with the manifest package name) the official unique public name for that component, as represented by the ComponentName class.

Changing the component name inside of an application can have negative consequences for your users. Some examples are:

  • If the name of a main activity of your application is changed, any shortcuts the user made to it will no longer work. A shortcut is an Intent that directly specifies the ComponentName it should run.

  • If the name of a service implementing a Live Wallpaper changes, then a user who has enabled your Live Wallpaper will have their wallpaper revert to the system default when getting the new version of your app. The same is true for Input Methods, Accessibility Services, Honeycomb’s new advanced Widgets, and so on.

  • If the name of a receiver implementing a Device Admin changes, then as with the live wallpaper example, the device admin will be disabled when the application is updated. This also applies to other kinds of receivers, such as App Widgets.

These behaviors are an outcome of how the Intent system is used on Android. There are two main kinds of Intents:

  • Implicit Intents only specify �what� they should match, using actions, categories, data, MIME types, and so on. The exact components that they will find are only determined at run-time, by the Package Manager matching it against the current applications.

  • Explicit Intents specify a single explicit �who� they should match, through a ComponentName. Regardless of whatever else is in the Intent, it is only associated with the exact manifest package name and class name as given in its ComponentName.

Both of these types of Intents are important to how Android interacts with your application. A typical example of this is how users browse and select live wallpapers.

To let the user pick a live wallpaper, the first thing Android must do is show them a list of the available live wallpaper services. It does this by building an implicit Intent with the appropriate action for a live wallpaper and asking the Package Manager for all services that support this Intent. The result is then the list of live wallpapers shown to the user.

When the user actually selects a specific live wallpaper they want to use, however, Android now must build an explicit Intent that identifies that particular live wallpaper. This is what is handed to the WallpaperManager to tell it which wallpaper to show.

This is why changing the name of the component in your manifest will cause the wallpaper to disappear: the explicit Intent that was previously saved is now invalid because the ComponentName it references no longer exists. There is no information available to indicate what the new name of the component is. (For example consider if your application had two different live wallpaper services the user could select.) Instead, Android must treat that live wallpaper as uninstalled and revert to its default wallpaper.

This is how input methods, device administrators, account managers, app widgets, and even application shortcuts work. The ComponentName is the public unique name of the components you declare in your manifest, and must not change if they are visible to other applications.

In conclusion: There are some parts of your application that can not change. Please be careful.

Thursday, March 3, 2011

Fragments For All

[This post is by Xavier Ducrohet, Android SDK Tech Lead. — Tim Bray]

A few weeks ago, Dianne Hackborn wrote about the new Fragments API, a mechanism that makes it easier for applications to scale across a variety of screen sizes.

However, as Dianne noted, this new API, which is part of Honeycomb, does not help developers whose applications target earlier versions of Android.



Today we�ve released a static library that exposes the same Fragments API (as well as the new LoaderManager and a few other classes) so that applications compatible with Android 1.6 or later can use fragments to create tablet-compatible user interfaces.

This library is available through the SDK Updater; it�s called �Android Compatibility package�.