Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6c22507d authored by Daniel Applebaum's avatar Daniel Applebaum
Browse files

Implementation of a Receiver and Service to provide for the capability

to accept control from other Android applications.  Allows for
changing both Account-level and global settings.  Account-level
settings can be applied to a single Account or to all Accounts.

The file class file derived from src/com/fsck/k9/K9RemoteControl.java
will be bundled into a JAR file for use by external applications.

This facility will be used for:
Issue 215
Issue 730
Issue 864
Issue 884

parent 752b5c59
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -25,6 +25,12 @@
                android:label="@string/read_attachment_label"
                android:description="@string/read_attachment_desc"/>
    <uses-permission android:name="com.fsck.k9.permission.READ_ATTACHMENT"/>
    <permission android:name="com.fsck.k9.permission.REMOTE_CONTROL"
                 android:permissionGroup="android.permission-group.MESSAGES"
                 android:protectionLevel="dangerous"
                 android:label="@string/remote_control_label"
                 android:description="@string/remote_control_desc"/>
    <uses-permission android:name="com.fsck.k9.permission.REMOTE_CONTROL"/>
    <application android:icon="@drawable/icon" android:label="@string/app_name" android:name="K9"
        >
        <meta-data android:name="android.app.default_searchable"
@@ -219,6 +225,19 @@
                <action android:name="com.fsck.k9.service.BroadcastReceiver.scheduleIntent"/>
            </intent-filter>
        </receiver>
        <receiver android:name="com.fsck.k9.service.RemoteControlReceiver"
               android:enabled="true"
               android:permission="com.fsck.k9.permission.REMOTE_CONTROL"
               >
               <intent-filter>
                 <action android:name="com.fsck.k9.K9RemoteControl.set"/>

             </intent-filter>
             <intent-filter>
                <action android:name="com.fsck.k9.K9RemoteControl.requestAccounts"/>

             </intent-filter>
         </receiver>        
        <receiver android:name="com.fsck.k9.service.CoreReceiver"
              android:enabled="true"
              >
@@ -241,6 +260,12 @@
             android:enabled="true"
            >
         </service>
         <service
              android:name="com.fsck.k9.service.RemoteControlService"
              android:enabled="true"
              android:permission="com.fsck.k9.permission.REMOTE_CONTROL"
             >
          </service>
         <service
             android:name="com.fsck.k9.service.SleepService"
             android:enabled="true"
+3 −1
Original line number Diff line number Diff line
@@ -638,4 +638,6 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
    <string name="animations_title">Animation</string>
    <string name="animations_summary">Use gaudy visual effects</string>

    <string name="remote_control_label">K-9 Mail remote control</string>
    <string name="remote_control_desc">Allows this application to control K-9 Mail activities and settings.</string>
</resources>
+108 −0
Original line number Diff line number Diff line
package com.fsck.k9;
/**
 * Utillity definitions for Android applications to control the behavior of K-9 Mail.  All such applications must declare the following permission:
 * <uses-permission android:name="com.fsck.k9.permission.REMOTE_CONTROL"/>
 * in their AndroidManifest.xml
 * 
 * An application that wishes to act on a particular Account in K-9 needs to fetch the list of configured Accounts by broadcasting an
 * {@link Intent} using K9_REQUEST_ACCOUNTS as the Action.  The broadcast must be made using the {@link ContextWrapper}
 * sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, 
 * Handler scheduler, int initialCode, String initialData, Bundle initialExtras).sendOrderedBroadcast}
 * method in order to receive the list of Account UUIDs and descriptions that K-9 will provide.
 * 
 * @author Daniel I. Applebaum
 *
 */
public class K9RemoteControl
{
    /**
     * {@link Intent} Action to be sent to K-9 using {@link ContextWrapper.sendOrderedBroadcast} in order to fetch the list of configured Accounts.
     * The responseData will contain two String[] with keys K9_ACCOUNT_UUIDS and K9_ACCOUNT_DESCRIPTIONS 
     */
    public final static String K9_REQUEST_ACCOUNTS = "com.fsck.k9.K9RemoteControl.requestAccounts";
    public final static String K9_ACCOUNT_UUIDS = "com.fsck.k9.K9RemoteControl.accountUuids";
    public final static String K9_ACCOUNT_DESCRIPTIONS = "com.fsck.k9.K9RemoteControl.accountDescriptions";

    /**
     * The {@link {@link Intent}} Action to set in order to cause K-9 to check mail.  (Not yet implemented)
     */
    //public final static String K9_CHECK_MAIL = "com.fsck.k9.K9RemoteControl.checkMail";
    
    /**
     * The {@link {@link Intent}} Action to set when remotely changing K-9 Mail settings
     */
    public final static String K9_SET = "com.fsck.k9.K9RemoteControl.set";
    /**
     * The key of the {@link Intent} Extra to set to hold the UUID of a single Account's settings to change.  Used only if K9_ALL_ACCOUNTS
     * is absent or false.
     */
    public final static String K9_ACCOUNT_UUID = "com.fsck.k9.K9RemoteControl.accountUuid";
    /**
     * The key of the {@link Intent} Extra to set to control if the settings will apply to all Accounts, or to the one
     * specified with K9_ACCOUNT_UUID
     */
    public final static String K9_ALL_ACCOUNTS = "com.fsck.k9.K9RemoteControl.allAccounts";
    
    public final static String K9_ENABLED = "true";
    public final static String K9_DISABLED = "false";
    
    /*
     * Key for the {@link Intent} Extra for controlling whether notifications will be generated for new unread mail.
     * Acceptable values are K9_ENABLED and K9_DISABLED
     */
    public final static String K9_NOTIFICATION_ENABLED = "com.fsck.k9.K9RemoteControl.notificationEnabled";
    /*
     * Key for the {@link Intent} Extra for controlling whether K-9 will sound the ringtone for new unread mail.
     * Acceptable values are K9_ENABLED and K9_DISABLED
     */
    public final static String K9_RING_ENABLED = "com.fsck.k9.K9RemoteControl.ringEnabled";
    /*
     * Key for the {@link Intent} Extra for controlling whether K-9 will activate the vibrator for new unread mail.
     * Acceptable values are K9_ENABLED and K9_DISABLED
     */
    public final static String K9_VIBRATE_ENABLED = "com.fsck.k9.K9RemoteControl.notificationEnabled";
    
    public final static String K9_FOLDERS_NONE = "NONE";
    public final static String K9_FOLDERS_ALL = "ALL";
    public final static String K9_FOLDERS_FIRST_CLASS = "FIRST_CLASS";
    public final static String K9_FOLDERS_FIRST_AND_SECOND_CLASS = "FIRST_AND_SECOND_CLASS";
    public final static String K9_FOLDERS_NOT_SECOND_CLASS = "NOT_SECOND_CLASS";
    /**
     * Key for the {@link Intent} Extra to set for controlling which folders to be synchronized with Push.
     * Acceptable values are K9_FOLDERS_ALL, K9_FOLDERS_FIRST_CLASS, K9_FOLDERS_FIRST_AND_SECOND_CLASS,
     * K9_FOLDERS_NOT_SECOND_CLASS, K9_FOLDERS_NONE
     */
    public final static String K9_PUSH_CLASSES = "com.fsck.k9.K9RemoteControl.pushClasses";
    /**
     * Key for the {@link Intent} Extra to set for controlling which folders to be synchronized with Poll.
     * Acceptable values are K9_FOLDERS_ALL, K9_FOLDERS_FIRST_CLASS, K9_FOLDERS_FIRST_AND_SECOND_CLASS,
     * K9_FOLDERS_NOT_SECOND_CLASS, K9_FOLDERS_NONE
     */
    public final static String K9_POLL_CLASSES = "com.fsck.k9.K9RemoteControl.pollClasses";
    
    public final static String[] K9_POLL_FREQUENCIES = { "-1", "1", "5", "10", "15", "30", "60", "120", "180", "360", "720", "1440"};  
    /**
     * Key for the {@link Intent} Extra to set with the desired poll frequency.  The value is a String representing a number of minutes.
     * Acceptable values are available in K9_POLL_FREQUENCIES
     */
    public final static String K9_POLL_FREQUENCY = "com.fsck.k9.K9RemoteControl.pollFrequency";
   
    /**
     * Key for the {@link Intent} Extra to set for controlling K-9's global "Background sync" setting.
     * Acceptable values are K9_BACKGROUND_OPERATIONS_ALWAYS, K9_BACKGROUND_OPERATIONS_NEVER
     * K9_BACKGROUND_OPERATIONS_WHEN_CHECKED
     */
    public final static String K9_BACKGROUND_OPERATIONS = "com.fsck.k9.K9RemoteControl.backgroundOperations";
    public final static String K9_BACKGROUND_OPERATIONS_WHEN_CHECKED = "WHEN_CHECKED";
    public final static String K9_BACKGROUND_OPERATIONS_ALWAYS = "ALWAYS";
    public final static String K9_BACKGROUND_OPERATIONS_NEVER = "NEVER";
    
    /**
     * Key for the {@link Intent} Extra to set for controlling which display theme K-9 will use.  Acceptable values are
     * K9_THEME_LIGHT, K9_THEME_DARK
     */
    public final static String K9_THEME = "com.fsck.k9.K9RemoteControl.theme";
    public final static String K9_THEME_LIGHT = "LIGHT";
    public final static String K9_THEME_DARK = "DARK";
    
}
+57 −0
Original line number Diff line number Diff line

package com.fsck.k9.service;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.K9RemoteControl;
import com.fsck.k9.Preferences;

import static com.fsck.k9.K9RemoteControl.*;

public class RemoteControlReceiver extends CoreReceiver
{
    public Integer receive(Context context, Intent intent, Integer tmpWakeLockId)
    {
        if (K9.DEBUG)
            Log.i(K9.LOG_TAG, "RemoteControlReceiver.onReceive" + intent);

        if (K9RemoteControl.K9_SET.equals(intent.getAction()))
        {
            RemoteControlService.set(context, intent, tmpWakeLockId);
            tmpWakeLockId = null;
        }
        else if (K9RemoteControl.K9_REQUEST_ACCOUNTS.equals(intent.getAction()))
        {
            try
            {
                Preferences preferences = Preferences.getPreferences(context);
                Account[] accounts = preferences.getAccounts();
                String[] uuids = new String[accounts.length];
                String[] descriptions = new String[accounts.length];
                for (int i = 0; i < accounts.length; i++)
                {
                    Account account = accounts[i];
                
                    uuids[i] = account.getUuid();
                    descriptions[i] = account.getDescription();
                }
                Bundle bundle = getResultExtras(true);
                bundle.putStringArray(K9_ACCOUNT_UUIDS, uuids);
                bundle.putStringArray(K9_ACCOUNT_DESCRIPTIONS, descriptions);
            }
            catch (Exception e)
            {
                Log.e(K9.LOG_TAG, "Could not handle K9_RESPONSE_INTENT", e);
            }
            
        }
        
        return tmpWakeLockId;
    }

}
+167 −0
Original line number Diff line number Diff line
package com.fsck.k9.service;

import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.K9RemoteControl;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
import com.fsck.k9.Account.FolderMode;

import static com.fsck.k9.K9RemoteControl.*;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Log;
import android.widget.Toast;

public class RemoteControlService extends CoreService
{
    private final static String RESCHEDULE_ACTION = "com.fsck.k9.service.RemoteControlService.RESCHEDULE_ACTION";

    private final static String SET_ACTION = "com.fsck.k9.service.RemoteControlService.SET_ACTION";

    public static void set(Context context, Intent i, Integer wakeLockId)
    {
      //  Intent i = new Intent();
        i.setClass(context, RemoteControlService.class);
        i.setAction(RemoteControlService.SET_ACTION);
        addWakeLockId(i, wakeLockId);
        if (wakeLockId == null)
        {
            addWakeLock(context, i);
        }
        context.startService(i);
    }

    public static final int REMOTE_CONTROL_SERVICE_WAKE_LOCK_TIMEOUT = 20000;
    
    @Override
    public void startService(final Intent intent, final int startId)
    {
        if (K9.DEBUG)
            Log.i(K9.LOG_TAG, "RemoteControlService started with startId = " + startId);
            final Preferences preferences = Preferences.getPreferences(this);
            
            if (RESCHEDULE_ACTION.equals(intent.getAction()))
            {
                if (K9.DEBUG)
                    Log.i(K9.LOG_TAG, "RemoteControlService requesting MailService reschedule");
                MailService.actionReschedule(this, null);
            }
            else if (RemoteControlService.SET_ACTION.equals(intent.getAction()))
            {
                if (K9.DEBUG)
                    Log.i(K9.LOG_TAG, "RemoteControlService got request to change settings");
                execute(getApplication(), new Runnable()
                {
                    public void run()
                    {
                        try
                        {
                            String uuid = intent.getStringExtra(K9_ACCOUNT_UUID);
                            boolean allAccounts = intent.getBooleanExtra(K9_ALL_ACCOUNTS, false);
                            if (K9.DEBUG)
                            {
                                if (allAccounts)
                                {
                                    Log.i(K9.LOG_TAG, "RemoteControlService changing settings for all accounts");
                                }
                                else
                                {
                                    Log.i(K9.LOG_TAG, "RemoteControlService changing settings for account with UUID " + uuid);
                                }
                            }
                            Account[] accounts = preferences.getAccounts();
                            for (Account account : accounts)
                            {
                                if (allAccounts || account.getUuid().equals(uuid))
                                {
        
                                    if (K9.DEBUG)
                                        Log.i(K9.LOG_TAG, "RemoteControlService changing settings for account " + account.getDescription());
                                    
                                    String notificationEnabled = intent.getStringExtra(K9_NOTIFICATION_ENABLED);
                                    String ringEnabled = intent.getStringExtra(K9_RING_ENABLED);
                                    String vibrateEnabled = intent.getStringExtra(K9_VIBRATE_ENABLED);
                                    String pushClasses = intent.getStringExtra(K9_PUSH_CLASSES);
                                    String pollClasses = intent.getStringExtra(K9_POLL_CLASSES);
                                    String pollFrequency = intent.getStringExtra(K9_POLL_FREQUENCY);
                                  
                                    if (notificationEnabled != null)
                                    {
                                        account.setNotifyNewMail(Boolean.parseBoolean(notificationEnabled));
                                    }
                                    if (ringEnabled != null)
                                    {
                                        account.setRing(Boolean.parseBoolean(ringEnabled));
                                    }
                                    if (vibrateEnabled != null)
                                    {
                                        account.setVibrate(Boolean.parseBoolean(vibrateEnabled));
                                    }
                                    if (pushClasses != null)
                                    {
                                        account.setFolderPushMode(FolderMode.valueOf(pushClasses));
                                    }
                                    if (pollClasses != null)
                                    {
                                        account.setFolderSyncMode(FolderMode.valueOf(pollClasses));
                                    }
                                    if (pollFrequency != null)
                                    {
                                        String[] allowedFrequencies = getResources().getStringArray(R.array.account_settings_check_frequency_values);
                                        for (String allowedFrequency : allowedFrequencies)
                                        {
                                            if (allowedFrequency.equals(pollFrequency))
                                            {
                                                account.setAutomaticCheckIntervalMinutes(Integer.parseInt(allowedFrequency));
                                            }
                                        }
                                    }
                                    account.save(Preferences.getPreferences(RemoteControlService.this));
                                }
                            }
                            if (K9.DEBUG)
                                Log.i(K9.LOG_TAG, "RemoteControlService changing global settings");
                            
                            String backgroundOps = intent.getStringExtra(K9_BACKGROUND_OPERATIONS);
                            if (K9RemoteControl.K9_BACKGROUND_OPERATIONS_ALWAYS.equals(backgroundOps)
                                    || K9RemoteControl.K9_BACKGROUND_OPERATIONS_NEVER.equals(backgroundOps)
                                    || K9RemoteControl.K9_BACKGROUND_OPERATIONS_WHEN_CHECKED.equals(backgroundOps))
                            {
                                K9.setBackgroundOps(backgroundOps);
                            }

                            String theme = intent.getStringExtra(K9_THEME);
                            if (theme != null)
                            {
                                K9.setK9Theme(K9RemoteControl.K9_THEME_DARK.equals(theme) ? android.R.style.Theme : android.R.style.Theme_Light);
                            }
                            
                            SharedPreferences sPrefs = preferences.getPreferences();
                            
                            Editor editor = sPrefs.edit();
                            K9.save(editor);
                            editor.commit();
                            
                            Intent i = new Intent();
                            i.setClassName(getApplication().getPackageName(), "com.fsck.k9.service.RemoteControlService");
                            i.setAction(RESCHEDULE_ACTION);
                            long nextTime = System.currentTimeMillis() + 10000;
                            BootReceiver.scheduleIntent(RemoteControlService.this, nextTime, i);
                        }
                        catch (Exception e)
                        {
                            Log.e(K9.LOG_TAG, "Could not handle K9_SET", e);
                            Toast toast = Toast.makeText(RemoteControlService.this, e.getMessage(), Toast.LENGTH_LONG);
                            toast.show();
                        }
                    }
                }
                , RemoteControlService.REMOTE_CONTROL_SERVICE_WAKE_LOCK_TIMEOUT, startId);
            }
    }

}