Home » Android » Android: launch a custom Preference from a PreferenceActivity

Android: launch a custom Preference from a PreferenceActivity

Posted by: admin May 14, 2020 Leave a comment

Questions:

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> 
How to&Answers:

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.

Answer:

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.)

Answer:

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;
            }
        }
    }
}

Answer:

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!