10

I would like to be able launch a 2nd Preference screen from my PreferenceActivity. And in the 2nd Preference screen I'd like to use a predefined layout from xml. So, I have two questions:

How do I use an xml layout as the layout view of a Preference? How do I add this custom preference to a PreferenceActivity such that launched when tapped?

Thank you

*EDIT in response to alibi

I am trying to launch an activity from a preference screen, by declaring the activity to be launched in xml. This causes this exception:

 04-01 19:04:37.962: ERROR/AndroidRuntime(8061): android.content.ActivityNotFoundException: Unable to find explicit activity class {com.me/CustomPrefScreen}; have you declared this activity in your AndroidManifest.xml?

*Another update. However, if I replace PrefrenceScreen in settings.xml with some extension of Preference, which overrides onClick() to launch CustomPrefScreen, then everything works fine.

Main preferences activity:

public class MyPreferences extends PreferenceActivity 
{
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.settings);
    }
}

settings.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceScreen  
        android:summary="my summary" 
        android:title="my title">
        <intent android:action="android.intent.action.MAIN"
                    android:targetPackage="com.me"
                    android:targetClass="CustomPrefScreen"/>
    </PreferenceScreen>

</PreferenceScreen>

mainfest file

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.me"
    android:versionCode="1"
    android:versionName="1.0">
    <application 
        android:icon="@drawable/icon" 
        android:label="@string/app_name" 
        android:theme="@style/Theme.NoBackground">
        <activity 
            android:name=".MyApp"
            android:label="@string/app_name">
            <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
             </activity>
        <activity 
            android:name=".CustomPrefScreen"
            android:label="@string/app_name">
        </activity>
        <activity 
            android:name=".MyPreferences"
            android:label="@string/app_name">
        </activity>
    </application>
    <uses-sdk android:minSdkVersion="4" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest> 
ab11
  • 19,770
  • 42
  • 120
  • 207
  • 1
    Both things are well explained in the Api Demos example. Funny thing is that it's in the SDK directory, thus you have the examples in your hard drive right now. – Cristian Apr 01 '11 at 16:18
  • Do you absolutely need the other preferences to be a second activity? If you just want more than one screen perhaps simply using a PreferenceScreen work? – slund Apr 01 '11 at 16:34
  • @Slund. I would like to use a PreferenceScreen, but I'm not sure how to use a custom xml layout for it; it seems only Preference type views can be added to it? – ab11 Apr 01 '11 at 16:49
  • Aha. Now I understand the question. You want to have a preference screen that uses custom UI instead of standard FooPreference views. For that you most likely want to make your own MyPreference extends Preference. – slund Apr 01 '11 at 16:55
  • @Slund. Well, thats what I'm wondering how to do. Can I define layout for MyPreference, and will that be launched when it is clicked on? Or does MyPreference have to launch a new activity which uses my custom layout? – ab11 Apr 01 '11 at 17:02
  • @Christian. I found the AdvancedPrefence example in the Api Demos. This still generates layout from a preferences xml, could you direct me to an example of a Preference that renders from a custom layout? – ab11 Apr 01 '11 at 17:20

4 Answers4

18

One solution would be to extend a DialogPreference which allows the setting of a custom layout for the preference dialog. This way you have a preference listed and when you tap it, you get a dialog with your custom settings UI.

 <com.xyz.MyPreference 
           android:dialogLayout="@layout/yourlayout"
           android:dialogTitle="Dialog Title"
            android:dialogMessage="Dialog summary"
            android:key="preference_key"
            android:title="Preference Title"
            android:summary="Preference summary"
            android:defaultValue="Default Value" /> 

And the class

class MyPreference extends DialogPreference {
// along with constructors, you will want to override
    @Override
    protected void onBindDialogView(View view) {
        super.onBindDialogView(view);
        // view is your layout expanded and added to the dialog
            // find and hang on to your views here, add click listeners etc
            // basically things you would do in onCreate
        mTextView = (TextView)view.findViewById(R.Id.mytextview);
        }

        @Override
        protected void onDialogClosed(boolean positiveResult) {
           super.onDialogClosed(positiveResult);

            if (positiveResult) {
                // deal with persisting your values here
            }
        }
}

Obviously there are some other details, but this is the basic idea.

slund
  • 6,367
  • 2
  • 26
  • 19
  • I considered this, but really don't want to extend DialogPreference. I do not want the "ok" and "cancel" buttons, nor the dialog style. I'd like the custom Preference to have a similar style as the PreferenceActivity that launched it. – ab11 Apr 01 '11 at 17:31
  • Well you could do the same with Preference and set android:layout for the preference to be your xml layout. This way your layout shows in the preference screen. You can also just override the widget portion with android:widgetLayout. Then you listen on onBindView to attach listeners to your controls. – slund Apr 01 '11 at 17:50
  • Not to be difficult, but.. If I add a PreferenceScreen to my main PreferenceActivity, and then I set the layout of that PreferenceScreen, the custom layout shows within the main PreferenceActivity, and the PreferenceScreen title and summary doesn't show. I would prefer the title and summary to show, and the custom layout to show in a new window, when the preference is tapped. – ab11 Apr 01 '11 at 18:19
  • You are not being difficult, you just want what you want ;-) You could create a Preference extension and override onClick to fire off the intent to another PreferenceActivity. – slund Apr 01 '11 at 18:25
  • Similar to the answer given by alibi? – ab11 Apr 01 '11 at 18:27
  • Creating the new preference extension and overriding onClick works great. Thank you. – ab11 Apr 01 '11 at 19:30
7

alibi's solution - of defining an intent within a <PreferenceScreen> entry - worked for me, after much trial-and-error of the targetPackage and targetClass fields.

targetPackage needs to be the full path to the package name of my application (that is, the package= entry in the AndroidManifest.xml file). targetClass needs to be the full path to the Activity - INCLUDING the package name, even if the Activity is in the same package as the Application.

The AndroidManifest.xml file for the Application also (of course) needs an entry for the Activity. I didn't define an <intent-filter> for this entry, presumably because the action is MAIN (this was true whether the Activity was in the same or a different package than the Application).

Example: the Application's package is com.thissocialworld. The Activity I'd like to kick off from the PreferencesScreen is in a package called com.coolcommon and the Activity class is com.thissocialworld.SpecialPreferences. The entry within the <PreferenceScreen> looks like this:

<intent android:action="android.intent.action.MAIN"
 android:targetPackage="com.thissocialworld"
 android:targetClass="com.thissocialworld.SpecialPreferences"/>

I may try changing action.MAIN to action.PREFERENCES if it seems to be necessary to get access to the PreferencesManager.

(PS my first post here, I couldn't figure out how to post this as a comment to the discussion started by alibi.)

Phil Haigh
  • 4,522
  • 1
  • 25
  • 29
SR Austin
  • 71
  • 1
  • 2
5

You might want something similar to what I did to upload a photo from the gallery or camera.

package com.atClass.lmt;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.provider.MediaStore;
import android.util.Log;


public class Prefs extends PreferenceActivity{
    //public static final int FLAG_ACTIVITY_CLEAR_TOP = 1;
    private static final int MEDIA_IMAGE_REQUEST_CODE = 1;
    private static final int CAMERA_IMAGE_REQUEST_CODE = 2;
    public static Uri cImageUri;

    public static Context cContext;
    public static Activity cActivity;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.settings);

        this.cContext = (Context)this;
        this.cActivity = (Activity)this;

        Preference customPref = (Preference) findPreference("user_display_picture");
        customPref.setOnPreferenceClickListener(
                new OnPreferenceClickListener() {
                    public boolean onPreferenceClick(Preference preference) {
                        return imageUploadDialog();
                    }
                });
    }

    protected void onStop(){
        super.onStop();
        MapTools.createMapView(false);
        Lmt.serviceBinder.serviceThread("loginDevice");
    }

    public boolean imageUploadDialog(){
        final CharSequence[] items = {"Take picture now","Upload from gallery"};
        AlertDialog.Builder lAlertDialog = new AlertDialog.Builder(cContext);
        lAlertDialog.setTitle("Upload action");
        lAlertDialog.setCancelable(true);
        lAlertDialog.setItems(items,
                new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialogInterface, int i){
                //Toast.makeText(getApplicationContext(), "Selected item: " +i,  Toast.LENGTH_SHORT).show();
                if (i == 0){
                    attachCameraImage();
                }
                if (i == 1){
                    attachGalleryImage();
                }
            }
        });
        lAlertDialog.setIcon(R.drawable.click_to_url);
        lAlertDialog.show();
        return true;
    }

    public void attachGalleryImage(){
        Intent getImageFromGalleryIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI);
        startActivityForResult(getImageFromGalleryIntent, MEDIA_IMAGE_REQUEST_CODE);
    }

    public void attachCameraImage(){
        String fileName = "testphoto.jpg";
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.TITLE, fileName);
        values.put(MediaStore.Images.Media.DESCRIPTION,"Image capture by camera");
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        cImageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, cImageUri);
        intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
        startActivityForResult(intent, CAMERA_IMAGE_REQUEST_CODE);
    }

    protected final void onActivityResult(final int requestCode, final int resultCode, final Intent i) {
        Log.d(Global.TAG,"--> Received callback with:" + resultCode);
        super.onActivityResult(requestCode, resultCode, i);
        if(resultCode == RESULT_OK) {
            Log.d(Global.TAG,"--> Result OK with:" + requestCode);
            switch(requestCode) {
            case MEDIA_IMAGE_REQUEST_CODE:
                Log.d(Global.TAG,"--> MEDIA_IMAGE_REQUEST_CODE");
                Gui.GuiProgressDialog.showLoadingSpinner(cActivity);
                cImageUri = i.getData();
                if (cImageUri == null){Log.d(Global.TAG,"--> ImageURI is null!");}
                Lmt.serviceBinder.serviceThread("uploadMemberPicture");
                break;
            case CAMERA_IMAGE_REQUEST_CODE:
                Log.d(Global.TAG,"--> CAMERA_IMAGE_REQUEST_CODE");
                //cImageUri = i.getData();
                if (cImageUri == null){Log.d(Global.TAG,"--> ImageURI is null!");}
                Lmt.serviceBinder.serviceThread("uploadMemberPicture");
                break;
            }
        }
    }
}
Kevin Parker
  • 16,975
  • 20
  • 76
  • 105
0

You can use a custom activity for this. Just design the activity and include it as a PreferenceScreen in your preferences.

<PreferenceScreen  android:summary="@string/pref_summary" android:title="@string/title_summary">
  <intent android:action="android.intent.action.MAIN"
     android:targetPackage="targetPackage"
     android:targetClass="targetClass"/>
</PreferenceScreen>

Don't forget to register your activity in the manifest!

alibi
  • 866
  • 1
  • 8
  • 11
  • Thank you, this makes sense. However, it fails with the following exception, even though I'm absolutely certain I registered the activity in my manifest. Any idea why? "android.content.ActivityNotFoundException: Unable to find explicit activity class {com.me.CustomPreference}; have you declared this activity in your AndroidManifest.xml?" – ab11 Apr 01 '11 at 17:53
  • Not really. I couldn't get this suggestion to work, to define the new intent in xml. It does work fine if I extend Prefence and override onClick() to launch the new intent. – ab11 Apr 03 '11 at 12:26