Initial upload: clavier-ando-mibox
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
plugins { id 'com.android.application' }
|
||||
|
||||
android {
|
||||
namespace 'com.ando.miboxkeyboard.tv'
|
||||
compileSdk 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'com.ando.miboxkeyboard.tv'
|
||||
minSdk 23
|
||||
targetSdk 35
|
||||
versionCode 1
|
||||
versionName '1.0'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:label="Clavier Ando MiBox"
|
||||
android:theme="@style/AppTheme"
|
||||
android:usesCleartextTraffic="true">
|
||||
|
||||
<activity
|
||||
android:name=".SetupActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".PickerActivity"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true" />
|
||||
|
||||
<service
|
||||
android:name=".AndoServerService"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="dataSync" />
|
||||
|
||||
<receiver
|
||||
android:name=".BootReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name=".RemoteKeyboardImeService"
|
||||
android:directBootAware="true"
|
||||
android:exported="true"
|
||||
android:label="Clavier Ando MiBox"
|
||||
android:permission="android.permission.BIND_INPUT_METHOD">
|
||||
<intent-filter>
|
||||
<action android:name="android.view.InputMethod" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.view.im"
|
||||
android:resource="@xml/method" />
|
||||
</service>
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.ando.miboxkeyboard.tv;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
|
||||
public class AndoServerService extends Service {
|
||||
private static final String CHANNEL_ID = "ando_mibox_keyboard_server";
|
||||
private static final int NOTIFICATION_ID = 8765;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
startAsForeground();
|
||||
RemoteCommandServer.start(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
startAsForeground();
|
||||
RemoteCommandServer.start(this);
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
private void startAsForeground() {
|
||||
try {
|
||||
createNotificationChannel();
|
||||
Notification notification;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
notification = new Notification.Builder(this, CHANNEL_ID)
|
||||
.setSmallIcon(android.R.drawable.ic_dialog_info)
|
||||
.setContentTitle("Clavier Ando MiBox")
|
||||
.setContentText("Serveur clavier actif sur le port 8765")
|
||||
.setOngoing(true)
|
||||
.build();
|
||||
} else {
|
||||
notification = new Notification.Builder(this)
|
||||
.setSmallIcon(android.R.drawable.ic_dialog_info)
|
||||
.setContentTitle("Clavier Ando MiBox")
|
||||
.setContentText("Serveur clavier actif sur le port 8765")
|
||||
.setOngoing(true)
|
||||
.build();
|
||||
}
|
||||
startForeground(NOTIFICATION_ID, notification);
|
||||
} catch (Exception ignored) {
|
||||
// If foreground mode is refused by the system, keep trying to start the HTTP server anyway.
|
||||
}
|
||||
}
|
||||
|
||||
private void createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (manager != null) {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"Serveur Clavier Ando MiBox",
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
channel.setDescription("Maintient le serveur clavier actif pour le smartphone.");
|
||||
manager.createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.ando.miboxkeyboard.tv;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
public class BootReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent == null) return;
|
||||
String action = intent.getAction();
|
||||
if (Intent.ACTION_BOOT_COMPLETED.equals(action) || Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)) {
|
||||
startServer(context);
|
||||
}
|
||||
}
|
||||
|
||||
static void startServer(Context context) {
|
||||
if (context == null) return;
|
||||
Intent serviceIntent = new Intent(context, AndoServerService.class);
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(serviceIntent);
|
||||
} else {
|
||||
context.startService(serviceIntent);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
try {
|
||||
context.startService(serviceIntent);
|
||||
} catch (Exception ignoredAgain) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.ando.miboxkeyboard.tv;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
public class PickerActivity extends Activity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (imm != null) {
|
||||
imm.showInputMethodPicker();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
}, 1200);
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
package com.ando.miboxkeyboard.tv;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class RemoteCommandServer {
|
||||
private static final int PORT = 8765;
|
||||
private static final Object LOCK = new Object();
|
||||
private static volatile boolean serverStarted = false;
|
||||
private static Context appContext;
|
||||
private static Thread serverThread;
|
||||
|
||||
private RemoteCommandServer() {}
|
||||
|
||||
public static void start(Context context) {
|
||||
if (context != null) {
|
||||
appContext = context.getApplicationContext();
|
||||
}
|
||||
synchronized (LOCK) {
|
||||
if (serverStarted) return;
|
||||
serverStarted = true;
|
||||
serverThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
runServer();
|
||||
}
|
||||
}, "AndoMiBoxAlwaysOnHttpServer");
|
||||
serverThread.setDaemon(true);
|
||||
serverThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
private static void runServer() {
|
||||
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
|
||||
while (true) {
|
||||
Socket socket = serverSocket.accept();
|
||||
handleClient(socket);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
serverStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleClient(Socket socket) {
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
|
||||
String requestLine = reader.readLine();
|
||||
if (requestLine == null) {
|
||||
socket.close();
|
||||
return;
|
||||
}
|
||||
|
||||
int contentLength = 0;
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null && line.length() > 0) {
|
||||
String lower = line.toLowerCase();
|
||||
if (lower.startsWith("content-length:")) {
|
||||
contentLength = Integer.parseInt(line.substring(15).trim());
|
||||
}
|
||||
}
|
||||
|
||||
char[] bodyChars = new char[Math.max(contentLength, 0)];
|
||||
int read = 0;
|
||||
while (read < contentLength) {
|
||||
int r = reader.read(bodyChars, read, contentLength - read);
|
||||
if (r < 0) break;
|
||||
read += r;
|
||||
}
|
||||
String body = new String(bodyChars, 0, read);
|
||||
|
||||
String response;
|
||||
int status = 200;
|
||||
Runnable afterResponse = null;
|
||||
|
||||
if (requestLine.startsWith("OPTIONS ")) {
|
||||
response = "OK options";
|
||||
} else if (requestLine.contains("/type")) {
|
||||
String text = extractText(requestLine, body);
|
||||
boolean ok = RemoteKeyboardImeService.typeFromServer(text);
|
||||
response = ok ? "OK typed" : "NO active keyboard";
|
||||
} else if (requestLine.contains("/enter")) {
|
||||
boolean ok = RemoteKeyboardImeService.sendKeyFromServer(KeyEvent.KEYCODE_ENTER);
|
||||
response = ok ? "OK enter" : "NO active keyboard";
|
||||
} else if (requestLine.contains("/backspace")) {
|
||||
boolean ok = RemoteKeyboardImeService.sendKeyFromServer(KeyEvent.KEYCODE_DEL);
|
||||
response = ok ? "OK backspace" : "NO active keyboard";
|
||||
} else if (requestLine.contains("/picker")) {
|
||||
response = "OK picker";
|
||||
afterResponse = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
showKeyboardPickerDelayed();
|
||||
}
|
||||
};
|
||||
} else if (requestLine.contains("/settings")) {
|
||||
response = "OK settings";
|
||||
afterResponse = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
openKeyboardSettingsDelayed();
|
||||
}
|
||||
};
|
||||
} else if (requestLine.contains("/ping")) {
|
||||
response = "OK Clavier Ando MiBox server";
|
||||
} else {
|
||||
status = 404;
|
||||
response = "Not found";
|
||||
}
|
||||
|
||||
writeResponse(socket, status, response);
|
||||
|
||||
if (afterResponse != null) {
|
||||
afterResponse.run();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
try { writeResponse(socket, 500, "Error"); } catch (Exception ignoredAgain) {}
|
||||
} finally {
|
||||
try { socket.close(); } catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
private static String extractText(String requestLine, String body) throws Exception {
|
||||
String text = body == null ? "" : body;
|
||||
|
||||
int idx = requestLine.indexOf("text=");
|
||||
if (idx >= 0) {
|
||||
String part = requestLine.substring(idx + 5);
|
||||
int end = part.indexOf(' ');
|
||||
if (end >= 0) part = part.substring(0, end);
|
||||
int amp = part.indexOf('&');
|
||||
if (amp >= 0) part = part.substring(0, amp);
|
||||
text = URLDecoder.decode(part, "UTF-8");
|
||||
}
|
||||
|
||||
if (text.trim().startsWith("{")) {
|
||||
Matcher m = Pattern.compile("\\\"text\\\"\\s*:\\s*\\\"(.*?)\\\"").matcher(text);
|
||||
if (m.find()) {
|
||||
text = m.group(1)
|
||||
.replace("\\n", "\n")
|
||||
.replace("\\\"", "\"")
|
||||
.replace("\\\\", "\\");
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private static void showKeyboardPickerDelayed() {
|
||||
if (appContext == null) return;
|
||||
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Intent intent = new Intent(appContext, PickerActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
appContext.startActivity(intent);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
|
||||
private static void openKeyboardSettingsDelayed() {
|
||||
final Context context = appContext;
|
||||
if (context == null) return;
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}, 250);
|
||||
}
|
||||
|
||||
private static void writeResponse(Socket socket, int status, String body) throws Exception {
|
||||
String statusText = status == 200 ? "OK" : status == 404 ? "Not Found" : "Internal Server Error";
|
||||
byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
|
||||
String headers = "HTTP/1.1 " + status + " " + statusText + "\r\n" +
|
||||
"Content-Type: text/plain; charset=utf-8\r\n" +
|
||||
"Access-Control-Allow-Origin: *\r\n" +
|
||||
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n" +
|
||||
"Access-Control-Allow-Headers: Content-Type\r\n" +
|
||||
"Content-Length: " + bytes.length + "\r\n" +
|
||||
"Connection: close\r\n\r\n";
|
||||
OutputStream out = socket.getOutputStream();
|
||||
out.write(headers.getBytes(StandardCharsets.UTF_8));
|
||||
out.write(bytes);
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.ando.miboxkeyboard.tv;
|
||||
|
||||
import android.inputmethodservice.InputMethodService;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class RemoteKeyboardImeService extends InputMethodService {
|
||||
private static RemoteKeyboardImeService activeService;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
activeService = this;
|
||||
RemoteCommandServer.start(this);
|
||||
BootReceiver.startServer(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (activeService == this) activeService = null;
|
||||
BootReceiver.startServer(this);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateInputView() {
|
||||
BootReceiver.startServer(this);
|
||||
TextView view = new TextView(this);
|
||||
view.setText("Clavier Ando MiBox actif - envoyer depuis le téléphone");
|
||||
view.setTextSize(16);
|
||||
view.setPadding(20, 20, 20, 20);
|
||||
return view;
|
||||
}
|
||||
|
||||
public static boolean typeFromServer(final String text) {
|
||||
final RemoteKeyboardImeService service = activeService;
|
||||
if (service == null || text == null) return false;
|
||||
service.runOnMainThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
InputConnection ic = service.getCurrentInputConnection();
|
||||
if (ic != null) ic.commitText(text, 1);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean sendKeyFromServer(final int keyCode) {
|
||||
final RemoteKeyboardImeService service = activeService;
|
||||
if (service == null) return false;
|
||||
service.runOnMainThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
InputConnection ic = service.getCurrentInputConnection();
|
||||
if (ic != null) {
|
||||
ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
|
||||
ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
private void runOnMainThread(Runnable runnable) {
|
||||
new android.os.Handler(getMainLooper()).post(runnable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.ando.miboxkeyboard.tv;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class SetupActivity extends Activity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
RemoteCommandServer.start(this);
|
||||
BootReceiver.startServer(this);
|
||||
|
||||
LinearLayout root = new LinearLayout(this);
|
||||
root.setOrientation(LinearLayout.VERTICAL);
|
||||
root.setPadding(40, 40, 40, 40);
|
||||
root.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
|
||||
TextView title = new TextView(this);
|
||||
title.setText("Clavier Ando MiBox");
|
||||
title.setTextSize(28);
|
||||
title.setGravity(Gravity.CENTER);
|
||||
root.addView(title, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
TextView info = new TextView(this);
|
||||
info.setText("Serveur en premier plan actif sur le port 8765.\n\n" +
|
||||
"Le bouton du smartphone peut ouvrir le sélecteur clavier même si Gboard est actif et même depuis une autre app.\n\n" +
|
||||
"Commandes :\n" +
|
||||
"- /picker : choisir le clavier actif\n" +
|
||||
"- /settings : paramètres clavier\n" +
|
||||
"- /type : écrire si Clavier Ando MiBox est actif\n" +
|
||||
"- /enter et /backspace");
|
||||
info.setTextSize(18);
|
||||
info.setPadding(0, 30, 0, 30);
|
||||
root.addView(info, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
Button startServer = new Button(this);
|
||||
startServer.setText("Démarrer / garder le serveur actif");
|
||||
startServer.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
RemoteCommandServer.start(SetupActivity.this);
|
||||
BootReceiver.startServer(SetupActivity.this);
|
||||
}
|
||||
});
|
||||
root.addView(startServer, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
Button openSettings = new Button(this);
|
||||
openSettings.setText("Ouvrir les paramètres clavier");
|
||||
openSettings.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
startActivity(new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS));
|
||||
}
|
||||
});
|
||||
root.addView(openSettings, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
Button chooseKeyboard = new Button(this);
|
||||
chooseKeyboard.setText("Choisir le clavier actif");
|
||||
chooseKeyboard.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
RemoteCommandServer.start(SetupActivity.this);
|
||||
BootReceiver.startServer(SetupActivity.this);
|
||||
((android.view.inputmethod.InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).showInputMethodPicker();
|
||||
}
|
||||
});
|
||||
root.addView(chooseKeyboard, new LinearLayout.LayoutParams(-1, -2));
|
||||
|
||||
setContentView(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
RemoteCommandServer.start(this);
|
||||
BootReceiver.startServer(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">Clavier Ando MiBox</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<style name="AppTheme" parent="android:style/Theme.Material.Light.NoActionBar">
|
||||
<item name="android:fontFamily">sans</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<input-method xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:settingsActivity="com.ando.miboxkeyboard.tv.SetupActivity"
|
||||
android:supportsSwitchingToNextInputMethod="true">
|
||||
|
||||
<subtype
|
||||
android:label="@string/app_name"
|
||||
android:imeSubtypeLocale="fr_FR"
|
||||
android:imeSubtypeMode="keyboard" />
|
||||
|
||||
</input-method>
|
||||
Reference in New Issue
Block a user