Skip to content

Add permissions to skills and tie Skill with SkillInfo #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions skill/src/main/java/org/dicio/skill/FallbackSkill.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
package org.dicio.skill;

import androidx.annotation.Nullable;

import org.dicio.skill.chain.InputRecognizer;

public interface FallbackSkill extends Skill {
public abstract class FallbackSkill extends Skill {

/**
* @see Skill#Skill(SkillInfo)
*/
public FallbackSkill(@Nullable final SkillInfo skillInfo) {
super(skillInfo);
}

@Override
default InputRecognizer.Specificity specificity() {
public InputRecognizer.Specificity specificity() {
return InputRecognizer.Specificity.low; // useless
}

@Override
default float score() {
public float score() {
return 0; // useless
}
}
45 changes: 35 additions & 10 deletions skill/src/main/java/org/dicio/skill/Skill.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.dicio.skill;

import androidx.annotation.Nullable;

import org.dicio.skill.chain.InputRecognizer;
import org.dicio.skill.chain.IntermediateProcessor;
import org.dicio.skill.chain.OutputGenerator;
Expand All @@ -10,22 +12,45 @@
import java.util.Collections;
import java.util.List;

public interface Skill extends CleanableUp {
public abstract class Skill implements CleanableUp {

@Nullable
private final SkillInfo skillInfo;

/**
* @param skillInfo the {@link SkillInfo} object this {@link Skill} is being created with (using
* {@link SkillInfo#build(SkillContext)}), or {@code null} if this skill is
* not being built by a {@link SkillInfo}
*/
public Skill(@Nullable final SkillInfo skillInfo) {
this.skillInfo = skillInfo;
}

/**
* @return the {@link SkillInfo} object passed to the constructor {@link #Skill(SkillInfo)}
*/
@Nullable
public SkillInfo getSkillInfo() {
return skillInfo;
}


/**
* @see InputRecognizer#specificity()
*/
InputRecognizer.Specificity specificity();
public abstract InputRecognizer.Specificity specificity();

/**
* @see InputRecognizer#setInput(String, List, List)
*/
void setInput(String input, List<String> inputWords, List<String> normalizedWordKeys);
public abstract void setInput(String input,
List<String> inputWords,
List<String> normalizedWordKeys);

/**
* @see InputRecognizer#score()
*/
float score();
public abstract float score();

/**
* This will be called if this skill was deemed as the best one which could provide output for
Expand All @@ -37,28 +62,28 @@ public interface Skill extends CleanableUp {
* @see IntermediateProcessor#process(Object, SkillContext)
* @param context the skill context with useful resources, see {@link SkillContext}
*/
void processInput(SkillContext context) throws Exception;
public abstract void processInput(SkillContext context) throws Exception;

/**
* @see OutputGenerator#generate(Object, SkillContext, SpeechOutputDevice,
* GraphicalOutputDevice)
*/
void generateOutput(SkillContext context,
SpeechOutputDevice speechOutputDevice,
GraphicalOutputDevice graphicalOutputDevice);
public abstract void generateOutput(SkillContext context,
SpeechOutputDevice speechOutputDevice,
GraphicalOutputDevice graphicalOutputDevice);

/**
* To prevent excessive memory usage, release all temporary resources and set to {@code null}
* all temporary variables used while calculating the score, getting the result or generating
* output.
*/
@Override
void cleanup();
public abstract void cleanup();

/**
* @see OutputGenerator#nextSkills()
*/
default List<Skill> nextSkills() {
public List<Skill> nextSkills() {
return Collections.emptyList();
}
}
19 changes: 19 additions & 0 deletions skill/src/main/java/org/dicio/skill/SkillInfo.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package org.dicio.skill;

import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment;

import java.util.Collections;
import java.util.List;

public abstract class SkillInfo {

private final String id;
Expand Down Expand Up @@ -106,4 +110,19 @@ public final boolean hasPreferences() {
*/
@Nullable
public abstract Fragment getPreferenceFragment();

/**
* Provides all of the permissions this skill needs in order to run. For example, the telephone
* skill needs the {@code CALL_PHONE} and {@code READ_CONTACTS} permissions to run. The
* permissions expressed here will be requested to the user when the skill is first used, or
* via settings. A skill should therefore be able to be built with {@link #build(SkillContext)}
* without any permission, and a skill's input scoring (i.e. {@link Skill#score()} and the
* related methods) should also work without permissions.
* @return all of the special permissions this skill requires, or an empty list if no special
* permissions are needed
*/
@NonNull
public List<String> getNeededPermissions() {
return Collections.emptyList();
}
}
17 changes: 14 additions & 3 deletions skill/src/main/java/org/dicio/skill/chain/ChainSkill.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
package org.dicio.skill.chain;

import androidx.annotation.Nullable;

import org.dicio.skill.Skill;
import org.dicio.skill.SkillContext;
import org.dicio.skill.SkillInfo;
import org.dicio.skill.output.GraphicalOutputDevice;
import org.dicio.skill.output.SpeechOutputDevice;

import java.util.ArrayList;
import java.util.List;

@SuppressWarnings({"rawtypes", "unchecked"})
public class ChainSkill implements Skill {
public class ChainSkill extends Skill {

public static class Builder {
ChainSkill chainSkill = new ChainSkill();
final ChainSkill chainSkill;

/**
* @see Skill#Skill(SkillInfo)
*/
public Builder(@Nullable final SkillInfo skillInfo) {
this.chainSkill = new ChainSkill(skillInfo);
}

public Builder recognize(final InputRecognizer<?> inputRecognizer) {
if (chainSkill.inputRecognizer != null) {
Expand Down Expand Up @@ -48,7 +58,8 @@ public ChainSkill output(final OutputGenerator<?> outputGenerator) {
private OutputGenerator outputGenerator;
private Object lastResult;

private ChainSkill() {
private ChainSkill(@Nullable final SkillInfo skillInfo) {
super(skillInfo);
intermediateProcessors = new ArrayList<>();
}

Expand Down
54 changes: 54 additions & 0 deletions skill/src/test/java/org/dicio/skill/FallbackSkillTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.dicio.skill;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;

import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import org.dicio.skill.chain.InputRecognizer;
import org.dicio.skill.output.GraphicalOutputDevice;
import org.dicio.skill.output.SpeechOutputDevice;
import org.junit.Test;

import java.util.List;

public class FallbackSkillTest {

@Test
public void testConstructorAndGetSkillInfo() {
final SkillInfo skillInfo = new SkillInfo("id", 0, 0, 0, false) {
@Override public boolean isAvailable(final SkillContext context) { return false; }
@Override public Skill build(final SkillContext context) { return null; }
@Nullable @Override public Fragment getPreferenceFragment() { return null; }
};

final Skill skill = new FallbackSkill(skillInfo) {
@Override public void setInput(final String input, final List<String> inputWords,
final List<String> normalizedWordKeys) {}
@Override public void processInput(final SkillContext context) {}
@Override public void generateOutput(final SkillContext context,
final SpeechOutputDevice speechOutputDevice,
final GraphicalOutputDevice graphicalOutputDevice) {}
@Override public void cleanup() {}
};

assertSame(skillInfo, skill.getSkillInfo());
}

@Test
public void testScoreAndSpecificity() {
final Skill skill = new FallbackSkill(null) {
@Override public void setInput(final String input, final List<String> inputWords,
final List<String> normalizedWordKeys) {}
@Override public void processInput(final SkillContext context) {}
@Override public void generateOutput(final SkillContext context,
final SpeechOutputDevice speechOutputDevice,
final GraphicalOutputDevice graphicalOutputDevice) {}
@Override public void cleanup() {}
};

assertEquals(0.0f, skill.score(), 0.0f);
assertSame(InputRecognizer.Specificity.low, skill.specificity());
}
}
45 changes: 45 additions & 0 deletions skill/src/test/java/org/dicio/skill/SkillInfoTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.dicio.skill;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import org.junit.Test;

import java.util.Arrays;
import java.util.List;

public class SkillInfoTest {
@Test
public void testConstructorAndGetters() {
final SkillInfo skillInfo = new SkillInfo("testId", 11, 222, 0, true) {
@Override public boolean isAvailable(final SkillContext context) { return false; }
@Override public Skill build(final SkillContext context) { return null; }
@Nullable @Override public Fragment getPreferenceFragment() { return null; }
};

assertSame("testId", skillInfo.getId());
assertEquals(11, skillInfo.getNameResource());
assertEquals(222, skillInfo.getSentenceExampleResource());
assertEquals(0, skillInfo.getIconResource());
assertTrue(skillInfo.hasPreferences());
}

@Test
public void testGetNeededPermissions() {
final SkillInfo skillInfo = new SkillInfo("", 0, 0, 0, false) {
@Override public boolean isAvailable(final SkillContext context) { return false; }
@Override public Skill build(final SkillContext context) { return null; }
@Nullable @Override public Fragment getPreferenceFragment() { return null; }
};

final List<String> permissions = skillInfo.getNeededPermissions();
assertNotNull(permissions);
assertTrue("Default permissions are not empty: " + Arrays.toString(permissions.toArray()),
permissions.isEmpty());
}
}
39 changes: 39 additions & 0 deletions skill/src/test/java/org/dicio/skill/SkillTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.dicio.skill;

import static org.junit.Assert.assertSame;

import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import org.dicio.skill.chain.InputRecognizer;
import org.dicio.skill.output.GraphicalOutputDevice;
import org.dicio.skill.output.SpeechOutputDevice;
import org.junit.Test;

import java.util.List;

public class SkillTest {

@Test
public void testConstructorAndGetSkillInfo() {
final SkillInfo skillInfo = new SkillInfo("id", 0, 0, 0, false) {
@Override public boolean isAvailable(final SkillContext context) { return false; }
@Override public Skill build(final SkillContext context) { return null; }
@Nullable @Override public Fragment getPreferenceFragment() { return null; }
};

final Skill skill = new Skill(skillInfo) {
@Override public InputRecognizer.Specificity specificity() { return null; }
@Override public void setInput(final String input, final List<String> inputWords,
final List<String> normalizedWordKeys) {}
@Override public float score() { return 0; }
@Override public void processInput(final SkillContext context) {}
@Override public void generateOutput(final SkillContext context,
final SpeechOutputDevice speechOutputDevice,
final GraphicalOutputDevice graphicalOutputDevice) {}
@Override public void cleanup() {}
};

assertSame(skillInfo, skill.getSkillInfo());
}
}
Loading