Skip to content

Commit 647efe6

Browse files
author
jadepeng
committed
init
0 parents  commit 647efe6

54 files changed

Lines changed: 867 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.idea
2+
build
3+
.gradle
4+
*.iml

build.gradle

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
plugins {
2+
id 'java'
3+
id 'org.jetbrains.intellij' version '0.4.21'
4+
}
5+
6+
group 'com.github.jadepeng.rainbowfart'
7+
version '1.0-SNAPSHOT'
8+
9+
sourceCompatibility = 1.8
10+
11+
repositories {
12+
mavenLocal()
13+
maven { url "https://maven.aliyun.com/nexus/content/groups/public/" }
14+
maven { url "https://repo.eclipse.org/content/groups/releases/" }
15+
maven { url "https://www.jetbrains.com/intellij-repository/releases" }
16+
maven { url "https://www.jetbrains.com/intellij-repository/snapshots" }
17+
}
18+
19+
dependencies {
20+
testCompile group: 'junit', name: 'junit', version: '4.12'
21+
implementation('javazoom:jlayer:1.0.1')
22+
}
23+
24+
intellij {
25+
version '2018.2.4'
26+
}
27+
patchPluginXml {
28+
changeNotes """
29+
Add change notes here.<br>
30+
<em>most HTML tags may be used</em>"""
31+
}

settings.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
rootProject.name = 'rainbow-fart'
2+
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
package com.github.jadepeng.rainbowfart;
2+
3+
import com.github.jadepeng.rainbowfart.bean.Manifest;
4+
import com.google.common.util.concurrent.ThreadFactoryBuilder;
5+
import com.google.gson.Gson;
6+
import com.github.jadepeng.rainbowfart.settings.FartSettings;
7+
import javazoom.jl.decoder.JavaLayerException;
8+
import javazoom.jl.player.Player;
9+
import org.apache.commons.io.FileUtils;
10+
import org.apache.commons.io.IOUtils;
11+
import org.apache.commons.lang3.StringUtils;
12+
13+
import java.io.*;
14+
import java.net.URL;
15+
import java.nio.file.Paths;
16+
import java.util.*;
17+
import java.util.concurrent.*;
18+
19+
/**
20+
* app context
21+
*
22+
* @author jqpeng
23+
*/
24+
public class Context {
25+
26+
private static Manifest manifest;
27+
28+
/**
29+
* keyword->voices
30+
*/
31+
private static Map<String, List<String>> keyword2Voices;
32+
33+
final static String eachHour = "$time_each_hour";
34+
35+
static HashMap<String, String> schedule = new HashMap<String, String>() {{
36+
put("$time_morning", "0930");
37+
put("$time_before_noon", "1130");
38+
put("$time_noon", "1200");
39+
put("$time_after_noon", "1400");
40+
put("$time_evening", "2100");
41+
put("$time_midnight", "2330");
42+
}};
43+
44+
45+
/**
46+
* play in a single thread pool
47+
*/
48+
static ExecutorService playerTheadPool;
49+
50+
static ScheduledExecutorService scheduledExecutorService;
51+
52+
static {
53+
ThreadFactory playerFactory = new ThreadFactoryBuilder()
54+
.setNameFormat("player-pool-%d").build();
55+
playerTheadPool = new ThreadPoolExecutor(1, 1,
56+
0L, TimeUnit.MILLISECONDS,
57+
new LinkedBlockingQueue<>(1024), playerFactory, new ThreadPoolExecutor.AbortPolicy());
58+
}
59+
60+
/**
61+
* init
62+
*
63+
* @param manifest
64+
*/
65+
public static void init(Manifest manifest) {
66+
Context.manifest = manifest;
67+
keyword2Voices = new HashMap<>(128);
68+
69+
if (scheduledExecutorService != null) {
70+
// clear schedule
71+
scheduledExecutorService.shutdown();
72+
}
73+
scheduledExecutorService = new ScheduledThreadPoolExecutor(10);
74+
75+
Date now = new Date();
76+
Calendar tomorrow = new GregorianCalendar();
77+
tomorrow.setTime(now);
78+
tomorrow.add(Calendar.DATE, 1);
79+
Calendar today = new GregorianCalendar();
80+
today.setTime(now);
81+
manifest.getContributes().stream().parallel().forEach(contribute -> {
82+
contribute.getKeywords().forEach(keyword -> {
83+
try {
84+
scheduleTimerTask(tomorrow, today, now, keyword);
85+
} catch (Exception ex) {
86+
ex.printStackTrace();
87+
}
88+
keyword2Voices.put(keyword, contribute.getVoices());
89+
});
90+
});
91+
}
92+
93+
94+
static String readVoicePackageJson(String name) throws IOException {
95+
FartSettings settings = FartSettings.getInstance();
96+
boolean isCustomer = StringUtils.isNotEmpty(settings.getCustomVoicePackage());
97+
if (isCustomer) {
98+
return FileUtils.readFileToString(Paths.get(settings.getCustomVoicePackage(), name).toFile(), "utf-8");
99+
}
100+
URL filePath = ResourcesLoader.class.getClassLoader().getResource("/build-in-voice-chinese/" + name);
101+
return IOUtils.toString(filePath.openStream(), "utf-8");
102+
}
103+
104+
/**
105+
* 加载配置
106+
*/
107+
public static void loadConfig() {
108+
try {
109+
//
110+
FartSettings settings = FartSettings.getInstance();
111+
if (!settings.isEnable()) {
112+
return;
113+
}
114+
String json = readVoicePackageJson("manifest.json");
115+
Gson gson = new Gson();
116+
Manifest manifest = gson.fromJson(json, Manifest.class);
117+
// load contributes.json
118+
if (manifest.getContributes() == null) {
119+
String contributesText = readVoicePackageJson("contributes.json");
120+
Manifest contributes = gson.fromJson(contributesText, Manifest.class);
121+
if (contributes.getContributes() != null) {
122+
manifest.setContributes(contributes.getContributes());
123+
}
124+
}
125+
Context.init(manifest);
126+
127+
} catch (IOException e) {
128+
}
129+
}
130+
131+
static void scheduleTimerTask(Calendar tomorrow, Calendar today, Date now, String type) {
132+
if (schedule.containsKey(type)) {
133+
String timeString = schedule.get(type);
134+
int hour = Integer.parseInt(timeString.substring(0, 2));
135+
int minutes = Integer.parseInt(timeString.substring(2, 4));
136+
tomorrow.set(Calendar.HOUR, hour);
137+
tomorrow.set(Calendar.MINUTE, minutes);
138+
today.set(Calendar.HOUR, hour);
139+
today.set(Calendar.MINUTE, minutes);
140+
long delay = now.getTime() > today.getTime().getTime() ? (tomorrow.getTime().getTime() - now.getTime()) : (today.getTime().getTime() - now.getTime());
141+
scheduledExecutorService.scheduleAtFixedRate(() -> {
142+
play(keyword2Voices.get(type));
143+
}, delay, 24 * 60 * 60 * 1000, TimeUnit.MILLISECONDS);
144+
} else if (type.equals(eachHour)) {
145+
int minutes = new Date().getMinutes();
146+
// 整点报时
147+
scheduledExecutorService.scheduleAtFixedRate(() -> {
148+
int hour = new Date().getHours();
149+
if (hour >= 10 && hour <= 17) {
150+
play(keyword2Voices.get(type));
151+
}
152+
//(60 - minutes) * 60 * 1000
153+
}, 600, 60 * 60 * 1000, TimeUnit.MILLISECONDS);
154+
}
155+
}
156+
157+
158+
/**
159+
* get Candidate voices
160+
*
161+
* @param inputHistory
162+
* @return
163+
*/
164+
public static List<String> getCandidate(String inputHistory) {
165+
166+
167+
final List<String> candidate = new ArrayList<>();
168+
169+
FartSettings settings = FartSettings.getInstance();
170+
if (!settings.isEnable()) {
171+
return candidate;
172+
}
173+
if (keyword2Voices != null) {
174+
keyword2Voices.forEach((keyword, voices) -> {
175+
if (inputHistory.contains(keyword)) {
176+
candidate.addAll(voices);
177+
}
178+
});
179+
}
180+
if (candidate.isEmpty()) {
181+
candidate.addAll(findSpecialKeyword(inputHistory));
182+
}
183+
return candidate;
184+
}
185+
186+
static List<String> findSpecialKeyword(String inputHistory) {
187+
List<String> candidate = new ArrayList<>();
188+
if (inputHistory.contains(":")) {
189+
String finalInputHistory = inputHistory.replace(":", "");
190+
schedule.forEach((key, time) -> {
191+
if (finalInputHistory.contains(time)) {
192+
if (keyword2Voices.containsKey(key)) {
193+
candidate.addAll(keyword2Voices.get(key));
194+
}
195+
}
196+
});
197+
}
198+
return candidate;
199+
}
200+
201+
public static void play(List<String> voices) {
202+
203+
FartSettings settings = FartSettings.getInstance();
204+
if (!settings.isEnable()) {
205+
return;
206+
}
207+
// play in single thread
208+
playerTheadPool.submit(() -> {
209+
String file = voices.get(new Random().nextInt() % voices.size());
210+
try {
211+
InputStream inputStream = null;
212+
if (StringUtils.isEmpty(settings.getCustomVoicePackage())) {
213+
inputStream = Context.class.getResourceAsStream("/build-in-voice-chinese/" + file);
214+
} else {
215+
File mp3File = Paths.get(settings.getCustomVoicePackage(), file).toFile();
216+
if (mp3File.exists()) {
217+
try {
218+
inputStream = new FileInputStream(mp3File);
219+
} catch (FileNotFoundException e) {
220+
}
221+
} else {
222+
return;
223+
}
224+
}
225+
if (inputStream != null) {
226+
Player player = new Player(inputStream);
227+
player.play();
228+
player.close();
229+
}
230+
} catch (JavaLayerException e) {
231+
}
232+
});
233+
}
234+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.github.jadepeng.rainbowfart;
2+
3+
import com.intellij.codeInsight.template.impl.editorActions.TypedActionHandlerBase;
4+
import com.intellij.openapi.actionSystem.DataContext;
5+
import com.intellij.openapi.editor.Editor;
6+
import com.intellij.openapi.editor.actionSystem.TypedActionHandler;
7+
import org.apache.commons.lang3.StringUtils;
8+
import org.jetbrains.annotations.NotNull;
9+
import org.jetbrains.annotations.Nullable;
10+
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
14+
public class RainbowFartTypedHandler extends TypedActionHandlerBase {
15+
16+
17+
private List<String> candidates = new ArrayList<>();
18+
19+
20+
public RainbowFartTypedHandler(@Nullable TypedActionHandler originalHandler) {
21+
super(originalHandler);
22+
}
23+
24+
/**
25+
* Processes a key typed in the editor. The handler is responsible for delegating to
26+
* the previously registered handler if it did not handle the typed key.
27+
*
28+
* @param editor the editor in which the key was typed.
29+
* @param charTyped the typed character.
30+
* @param dataContext the current data context.
31+
*/
32+
@Override
33+
public void execute(@NotNull Editor editor, char charTyped, @NotNull DataContext dataContext) {
34+
candidates.add(String.valueOf(charTyped));
35+
String str = StringUtils.join(candidates, "");
36+
try {
37+
List<String> voices = Context.getCandidate(str);
38+
if (!voices.isEmpty()) {
39+
Context.play(voices);
40+
candidates.clear();
41+
}
42+
}catch (Exception e){
43+
// TODO
44+
candidates.clear();
45+
}
46+
47+
if (this.myOriginalHandler != null) {
48+
this.myOriginalHandler.execute(editor, charTyped, dataContext);
49+
}
50+
}
51+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.github.jadepeng.rainbowfart;
2+
3+
import com.intellij.openapi.project.Project;
4+
import com.intellij.openapi.startup.StartupActivity;
5+
import org.jetbrains.annotations.NotNull;
6+
7+
8+
/**
9+
* 资源加载
10+
* @author jqpeng
11+
*/
12+
public class ResourcesLoader implements StartupActivity {
13+
14+
@Override
15+
public void runActivity(@NotNull Project project) {
16+
Context.loadConfig();
17+
}
18+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.github.jadepeng.rainbowfart.bean;
2+
3+
import java.util.List;
4+
5+
/**
6+
* 配置项
7+
* @author jqpeng
8+
*/
9+
public class Contribute {
10+
List<String> keywords;
11+
List<String> voices;
12+
13+
public List<String> getKeywords() {
14+
return keywords;
15+
}
16+
17+
public void setKeywords(List<String> keywords) {
18+
this.keywords = keywords;
19+
}
20+
21+
public List<String> getVoices() {
22+
return voices;
23+
}
24+
25+
public void setVoices(List<String> voices) {
26+
this.voices = voices;
27+
}
28+
}

0 commit comments

Comments
 (0)