Initial Commit v2.0.13 with MQTT Library adding.
6
.idea/compiler.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
25
.idea/jarRepositories.xml
generated
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="BintrayJCenter" />
|
||||
<option name="name" value="BintrayJCenter" />
|
||||
<option name="url" value="https://jcenter.bintray.com/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="Google" />
|
||||
<option name="name" value="Google" />
|
||||
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
23
.idea/misc.xml
generated
Normal file
@ -0,0 +1,23 @@
|
||||
<project version="4">
|
||||
<component name="CMakeSettings">
|
||||
<configurations>
|
||||
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
|
||||
</configurations>
|
||||
</component>
|
||||
<component name="DesignSurface">
|
||||
<option name="filePathToZoomLevelMap">
|
||||
<map>
|
||||
<entry key="..\:/Workspace/Android/OsicManager/app/src/main/res/drawable/selector_button.xml" value="0.2708333333333333" />
|
||||
<entry key="..\:/Workspace/Android/OsicManager/app/src/main/res/layout/activity_main.xml" value="0.1437389770723104" />
|
||||
<entry key="..\:/Workspace/Android/OsicManager/app/src/main/res/layout/activity_setting.xml" value="0.164021164021164" />
|
||||
<entry key="..\:/Workspace/Android/OsicManager/app/src/main/res/layout/fragment_setting.xml" value="0.25" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
155
app/build.gradle
Normal file
@ -0,0 +1,155 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.2"
|
||||
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('D:\\Android\\rito3399.jks')
|
||||
storePassword 'ritoseo'
|
||||
keyAlias = 'ritokey'
|
||||
keyPassword 'ritoseo'
|
||||
// storePassword 'android'
|
||||
// keyAlias = 'platform'
|
||||
// keyPassword 'android'
|
||||
}
|
||||
|
||||
release {
|
||||
storeFile file('D:\\Android\\rito3399.jks')
|
||||
storePassword 'ritoseo'
|
||||
keyAlias = 'ritokey'
|
||||
keyPassword 'ritoseo'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
defaultConfig {
|
||||
applicationId "kr.co.rito.osicmanager"
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
// For 119 Version
|
||||
// versionName "1.0.18"
|
||||
// For OSIC10
|
||||
// 2.0.2 - for YELLOW, BLUE KEY active mode working.
|
||||
// 2.0.3 - for active mode hsact:// working
|
||||
// 2.0.4 - for encoder live audio drop retry
|
||||
// 2.0.5 - for 액티브 모드에서 방송 대기화면 표출 나오는 이슈, 액티브 모드에서 스트림 재시도 후 종료되는 이슈 처리
|
||||
// 2.0.6 - for VFD 설정 변경 시 LongPress : 컨펌, ShortPress : 취소
|
||||
// 2.0.7 - for 웹뷰 로딩 에러시 대기이미지 표시, 스트리밍 문제 발생시 "신호가 약합니다." 표시, stbpage/osic10.php 변경. runChannelList -> lastPlayUrl 설정 추가
|
||||
// 2.0.8 - for ShouldStopPlay, ShouldPlayUrl 추가, 웹뷰 로딩 대기이미지 패시브 모드 출력 버그 수정
|
||||
// 2.0.9 - for 리모컨 정지 시 블랙화면 나오도록 수정 - 2024-06-20
|
||||
// 2.0.10 - for 플레이어 버퍼 로그 추가 - 2024-07-25 [ logmanager 연동 필요 ]
|
||||
// 2.0.11 - for MQTT 추가 ebcast 연동
|
||||
// 2.0.12 - for emergency.wav 변경
|
||||
// 2.0.13 - for AudioFilePlayer에서 MediaPlayer를 ExoPlayer로 변경. mEbcastMqttIp 없을 때 MQTT 실행 방지
|
||||
versionName "2.0.13"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
//ndk.abiFilters 'armeabi-v7a', 'arm64-v8a'
|
||||
ndk.abiFilters 'arm64-v8a'
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
//buildConfigField "String", "SPECIAL_APP_MODE", "\"119NOTI_DEMO\""
|
||||
buildConfigField "String", "SPECIAL_APP_MODE", "\"\""
|
||||
}
|
||||
|
||||
release {
|
||||
//buildConfigField "String", "SPECIAL_APP_MODE", "\"119NOTI_DEMO\""
|
||||
buildConfigField "String", "SPECIAL_APP_MODE", "\"\""
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "src/main/cpp/CMakeLists.txt"
|
||||
version "3.10.2"
|
||||
}
|
||||
}
|
||||
ndkVersion '21.0.6113669'
|
||||
|
||||
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.outputs.all {
|
||||
outputFileName = "OsicManager_v${defaultConfig.versionName}.apk"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.material:material:1.1.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
//compileOnly files('libs/ritocls.jar')
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
api fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
||||
//implementation 'org.videolan.android:libvlc-all:3.3.0-eap10'
|
||||
//implementation 'org.videolan.android:libvlc-all:3.4.0-eap4'
|
||||
implementation 'org.videolan.android:libvlc-all:3.4.5'
|
||||
implementation 'com.google.android.exoplayer:exoplayer:2.16.0'
|
||||
|
||||
// mqtt
|
||||
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
|
||||
//implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
|
||||
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
|
||||
// implementation 'com.mindorks.android:prdownloader:0.6.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
}
|
||||
|
||||
|
||||
//preBuild {
|
||||
// doLast {
|
||||
// //def imlFile = file(project.name + ".iml")
|
||||
// def imlFile = file("..\\.idea\\modules\\app\\" + project.parent.name + ".app.iml")
|
||||
// println 'Change ' + project.name + '.iml order'
|
||||
// try {
|
||||
// def parsedXml = (new XmlParser()).parse(imlFile)
|
||||
// def jdkNode = parsedXml.component[1].orderEntry.find { it.'@type' == 'jdk' }
|
||||
// parsedXml.component[1].remove(jdkNode)
|
||||
// def sdkString = "Android API " + android.compileSdkVersion.substring("android-".length()) + " Platform"
|
||||
// println 'what' + sdkString
|
||||
// new Node(parsedXml.component[1], 'orderEntry', ['type': 'jdk', 'jdkName': sdkString, 'jdkType': 'Android SDK'])
|
||||
// groovy.xml.XmlUtil.serialize(parsedXml, new FileOutputStream(imlFile))
|
||||
// } catch (FileNotFoundException e) {
|
||||
// // nop, iml not found
|
||||
// println "no iml found"
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
//preBuild {
|
||||
// doLast {
|
||||
// def imlFile = file(project.name + ".app.iml")
|
||||
// println 'Change ' + project.name + '.iml order'
|
||||
// try {
|
||||
// def parsedXml = (new XmlParser()).parse(imlFile)
|
||||
// def jdkNode = parsedXml.component[1].orderEntry.find { it.'@type' == 'jdk' }
|
||||
// parsedXml.component[1].remove(jdkNode)
|
||||
// def sdkString = "Android API " + android.compileSdkVersion.substring("android-".length()) + " Platform"
|
||||
// println 'what' + sdkString
|
||||
// new Node(parsedXml.component[1], 'orderEntry', ['type': 'jdk', 'jdkName': sdkString, 'jdkType': 'Android SDK'])
|
||||
// groovy.xml.XmlUtil.serialize(parsedXml, new FileOutputStream(imlFile))
|
||||
// } catch (FileNotFoundException e) {
|
||||
// // nop, iml not found
|
||||
// println "no iml found"
|
||||
// }
|
||||
// }
|
||||
//}
|
BIN
app/libs/libvlc-armv8-3.5.2-eap1.aar_
Normal file
BIN
app/libs/ritocls.jar
Normal file
BIN
app/libs/rplayer-lib-debug.aar
Normal file
BIN
app/libs/rplayer-lib-debug.aar_201130
Normal file
BIN
app/libs/rplayer-lib-debug.aar_211130
Normal file
@ -0,0 +1,27 @@
|
||||
package kr.co.rito.osicmanager;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
assertEquals("kr.co.rito.osicmanager", appContext.getPackageName());
|
||||
}
|
||||
}
|
42
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="kr.co.rito.osicmanager">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:clearTaskOnLaunch="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".SettingActivity"
|
||||
android:screenOrientation="landscape"
|
||||
android:label="@string/title_activity_setting"
|
||||
android:theme="@style/AppTheme.NoActionBar"></activity>
|
||||
<activity android:name=".MainActivity"
|
||||
android:screenOrientation="landscape"
|
||||
android:clearTaskOnLaunch="true"
|
||||
android:launchMode="singleInstance">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service android:name="org.eclipse.paho.android.service.MqttService" />
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
</manifest>
|
BIN
app/src/main/assets/blackground.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
app/src/main/assets/downchimes.wav
Normal file
BIN
app/src/main/assets/emergency.wav
Normal file
BIN
app/src/main/assets/emergency_old.wav
Normal file
BIN
app/src/main/assets/fire.wav
Normal file
BIN
app/src/main/assets/fire_ori.wav
Normal file
BIN
app/src/main/assets/general.wav
Normal file
BIN
app/src/main/assets/mute.wav
Normal file
BIN
app/src/main/assets/rescue.pk
Normal file
BIN
app/src/main/assets/rescue.wav
Normal file
BIN
app/src/main/assets/upchimes.wav
Normal file
44
app/src/main/cpp/CMakeLists.txt
Normal file
@ -0,0 +1,44 @@
|
||||
# For more information about using CMake with Android Studio, read the
|
||||
# documentation: https://d.android.com/studio/projects/add-native-code.html
|
||||
|
||||
# Sets the minimum version of CMake required to build the native library.
|
||||
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
# Creates and names a library, sets it as either STATIC
|
||||
# or SHARED, and provides the relative paths to its source code.
|
||||
# You can define multiple libraries, and CMake builds them for you.
|
||||
# Gradle automatically packages shared libraries with your APK.
|
||||
|
||||
add_library( # Sets the name of the library.
|
||||
osic-lib
|
||||
|
||||
# Sets the library as a shared library.
|
||||
SHARED
|
||||
|
||||
# Provides a relative path to your source file(s).
|
||||
native-lib.cpp osic-manager.cpp sha1sum.cpp mixer.c pcm.c)
|
||||
|
||||
# Searches for a specified prebuilt library and stores the path as a
|
||||
# variable. Because CMake includes system libraries in the search path by
|
||||
# default, you only need to specify the name of the public NDK library
|
||||
# you want to add. CMake verifies that the library exists before
|
||||
# completing its build.
|
||||
|
||||
find_library( # Sets the name of the path variable.
|
||||
log-lib
|
||||
|
||||
# Specifies the name of the NDK library that
|
||||
# you want CMake to locate.
|
||||
log )
|
||||
|
||||
# Specifies libraries CMake should link to your target library. You
|
||||
# can link multiple libraries, such as libraries you define in this
|
||||
# build script, prebuilt third-party libraries, or system libraries.
|
||||
|
||||
target_link_libraries( # Specifies the target library.
|
||||
osic-lib
|
||||
|
||||
# Links the target library to the log library
|
||||
# included in the NDK.
|
||||
${log-lib} )
|
628
app/src/main/cpp/mixer.c
Normal file
@ -0,0 +1,628 @@
|
||||
/* mixer.c
|
||||
**
|
||||
** Copyright 2011, The Android Open Source Project
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** * Neither the name of The Android Open Source Project nor the names of
|
||||
** its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
|
||||
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
|
||||
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
** DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#define __force
|
||||
#define __bitwise
|
||||
#define __user
|
||||
#include <sound/asound.h>
|
||||
|
||||
#include "tinyalsa/asoundlib.h"
|
||||
|
||||
struct mixer_ctl {
|
||||
struct mixer *mixer;
|
||||
struct snd_ctl_elem_info *info;
|
||||
char **ename;
|
||||
};
|
||||
|
||||
struct mixer {
|
||||
int fd;
|
||||
struct snd_ctl_card_info card_info;
|
||||
struct snd_ctl_elem_info *elem_info;
|
||||
struct mixer_ctl *ctl;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
void mixer_close(struct mixer *mixer)
|
||||
{
|
||||
unsigned int n,m;
|
||||
|
||||
if (!mixer)
|
||||
return;
|
||||
|
||||
if (mixer->fd >= 0)
|
||||
close(mixer->fd);
|
||||
|
||||
if (mixer->ctl) {
|
||||
for (n = 0; n < mixer->count; n++) {
|
||||
if (mixer->ctl[n].ename) {
|
||||
unsigned int max = mixer->ctl[n].info->value.enumerated.items;
|
||||
for (m = 0; m < max; m++)
|
||||
free(mixer->ctl[n].ename[m]);
|
||||
free(mixer->ctl[n].ename);
|
||||
}
|
||||
}
|
||||
free(mixer->ctl);
|
||||
}
|
||||
|
||||
if (mixer->elem_info)
|
||||
free(mixer->elem_info);
|
||||
|
||||
free(mixer);
|
||||
|
||||
/* TODO: verify frees */
|
||||
}
|
||||
|
||||
struct mixer *mixer_open(unsigned int card)
|
||||
{
|
||||
struct snd_ctl_elem_list elist;
|
||||
struct snd_ctl_elem_info tmp;
|
||||
struct snd_ctl_elem_id *eid = NULL;
|
||||
struct mixer *mixer = NULL;
|
||||
unsigned int n, m;
|
||||
int fd;
|
||||
char fn[256];
|
||||
|
||||
snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
|
||||
fd = open(fn, O_RDWR);
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
|
||||
memset(&elist, 0, sizeof(elist));
|
||||
if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
|
||||
goto fail;
|
||||
|
||||
mixer = calloc(1, sizeof(*mixer));
|
||||
if (!mixer)
|
||||
goto fail;
|
||||
|
||||
mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
|
||||
mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
|
||||
if (!mixer->ctl || !mixer->elem_info)
|
||||
goto fail;
|
||||
|
||||
if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
|
||||
goto fail;
|
||||
|
||||
eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
|
||||
if (!eid)
|
||||
goto fail;
|
||||
|
||||
mixer->count = elist.count;
|
||||
mixer->fd = fd;
|
||||
elist.space = mixer->count;
|
||||
elist.pids = eid;
|
||||
if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
|
||||
goto fail;
|
||||
|
||||
for (n = 0; n < mixer->count; n++) {
|
||||
struct snd_ctl_elem_info *ei = mixer->elem_info + n;
|
||||
ei->id.numid = eid[n].numid;
|
||||
if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
|
||||
goto fail;
|
||||
mixer->ctl[n].info = ei;
|
||||
mixer->ctl[n].mixer = mixer;
|
||||
if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
|
||||
char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
|
||||
if (!enames)
|
||||
goto fail;
|
||||
mixer->ctl[n].ename = enames;
|
||||
for (m = 0; m < ei->value.enumerated.items; m++) {
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
tmp.id.numid = ei->id.numid;
|
||||
tmp.value.enumerated.item = m;
|
||||
if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
|
||||
goto fail;
|
||||
enames[m] = strdup(tmp.value.enumerated.name);
|
||||
if (!enames[m])
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(eid);
|
||||
return mixer;
|
||||
|
||||
fail:
|
||||
/* TODO: verify frees in failure case */
|
||||
if (eid)
|
||||
free(eid);
|
||||
if (mixer)
|
||||
mixer_close(mixer);
|
||||
else if (fd >= 0)
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *mixer_get_name(struct mixer *mixer)
|
||||
{
|
||||
return (const char *)mixer->card_info.name;
|
||||
}
|
||||
|
||||
unsigned int mixer_get_num_ctls(struct mixer *mixer)
|
||||
{
|
||||
if (!mixer)
|
||||
return 0;
|
||||
|
||||
return mixer->count;
|
||||
}
|
||||
|
||||
struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
|
||||
{
|
||||
if (mixer && (id < mixer->count))
|
||||
return mixer->ctl + id;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
|
||||
{
|
||||
unsigned int n;
|
||||
|
||||
if (!mixer)
|
||||
return NULL;
|
||||
|
||||
for (n = 0; n < mixer->count; n++)
|
||||
if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
|
||||
return mixer->ctl + n;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mixer_ctl_update(struct mixer_ctl *ctl)
|
||||
{
|
||||
ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
|
||||
}
|
||||
|
||||
const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
|
||||
{
|
||||
if (!ctl)
|
||||
return NULL;
|
||||
|
||||
return (const char *)ctl->info->id.name;
|
||||
}
|
||||
|
||||
enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
|
||||
{
|
||||
if (!ctl)
|
||||
return MIXER_CTL_TYPE_UNKNOWN;
|
||||
|
||||
switch (ctl->info->type) {
|
||||
case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;
|
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;
|
||||
case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
|
||||
case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;
|
||||
case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;
|
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;
|
||||
default: return MIXER_CTL_TYPE_UNKNOWN;
|
||||
};
|
||||
}
|
||||
|
||||
const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
|
||||
{
|
||||
if (!ctl)
|
||||
return "";
|
||||
|
||||
switch (ctl->info->type) {
|
||||
case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
|
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";
|
||||
case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
|
||||
case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";
|
||||
case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
|
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
|
||||
default: return "Unknown";
|
||||
};
|
||||
}
|
||||
|
||||
unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
|
||||
{
|
||||
if (!ctl)
|
||||
return 0;
|
||||
|
||||
return ctl->info->count;
|
||||
}
|
||||
|
||||
static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
|
||||
{
|
||||
int range;
|
||||
|
||||
if (percent > 100)
|
||||
percent = 100;
|
||||
else if (percent < 0)
|
||||
percent = 0;
|
||||
|
||||
range = (ei->value.integer.max - ei->value.integer.min);
|
||||
|
||||
return ei->value.integer.min + (range * percent) / 100;
|
||||
}
|
||||
|
||||
static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
|
||||
{
|
||||
int range = (ei->value.integer.max - ei->value.integer.min);
|
||||
|
||||
if (range == 0)
|
||||
return 0;
|
||||
|
||||
return ((value - ei->value.integer.min) / range) * 100;
|
||||
}
|
||||
|
||||
int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
|
||||
{
|
||||
if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
|
||||
return -EINVAL;
|
||||
|
||||
return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
|
||||
}
|
||||
|
||||
int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
|
||||
{
|
||||
if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
|
||||
return -EINVAL;
|
||||
|
||||
return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
|
||||
}
|
||||
|
||||
int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
|
||||
{
|
||||
struct snd_ctl_elem_value ev;
|
||||
int ret;
|
||||
|
||||
if (!ctl || (id >= ctl->info->count))
|
||||
return -EINVAL;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.id.numid = ctl->info->id.numid;
|
||||
ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (ctl->info->type) {
|
||||
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
|
||||
return !!ev.value.integer.value[id];
|
||||
|
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER:
|
||||
return ev.value.integer.value[id];
|
||||
|
||||
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
|
||||
return ev.value.enumerated.item[id];
|
||||
|
||||
case SNDRV_CTL_ELEM_TYPE_BYTES:
|
||||
return ev.value.bytes.data[id];
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mixer_ctl_is_access_tlv_rw(struct mixer_ctl *ctl)
|
||||
{
|
||||
return (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
|
||||
}
|
||||
|
||||
int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
|
||||
{
|
||||
struct snd_ctl_elem_value ev;
|
||||
int ret = 0;
|
||||
size_t size;
|
||||
void *source;
|
||||
size_t total_count;
|
||||
|
||||
if ((!ctl) || !count || !array)
|
||||
return -EINVAL;
|
||||
|
||||
total_count = ctl->info->count;
|
||||
|
||||
if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
|
||||
mixer_ctl_is_access_tlv_rw(ctl)) {
|
||||
/* Additional two words is for the TLV header */
|
||||
total_count += TLV_HEADER_SIZE;
|
||||
}
|
||||
|
||||
if (count > total_count)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.id.numid = ctl->info->id.numid;
|
||||
|
||||
switch (ctl->info->type) {
|
||||
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
|
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER:
|
||||
ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
size = sizeof(ev.value.integer.value[0]);
|
||||
source = ev.value.integer.value;
|
||||
break;
|
||||
|
||||
case SNDRV_CTL_ELEM_TYPE_BYTES:
|
||||
/* check if this is new bytes TLV */
|
||||
if (mixer_ctl_is_access_tlv_rw(ctl)) {
|
||||
struct snd_ctl_tlv *tlv;
|
||||
int ret;
|
||||
|
||||
if (count > SIZE_MAX - sizeof(*tlv))
|
||||
return -EINVAL;
|
||||
tlv = calloc(1, sizeof(*tlv) + count);
|
||||
if (!tlv)
|
||||
return -ENOMEM;
|
||||
tlv->numid = ctl->info->id.numid;
|
||||
tlv->length = count;
|
||||
ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, tlv);
|
||||
|
||||
source = tlv->tlv;
|
||||
memcpy(array, source, count);
|
||||
|
||||
free(tlv);
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
size = sizeof(ev.value.bytes.data[0]);
|
||||
source = ev.value.bytes.data;
|
||||
break;
|
||||
}
|
||||
|
||||
case SNDRV_CTL_ELEM_TYPE_IEC958:
|
||||
size = sizeof(ev.value.iec958);
|
||||
source = &ev.value.iec958;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(array, source, size * count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
|
||||
{
|
||||
struct snd_ctl_elem_value ev;
|
||||
int ret;
|
||||
|
||||
if (!ctl || (id >= ctl->info->count))
|
||||
return -EINVAL;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.id.numid = ctl->info->id.numid;
|
||||
ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (ctl->info->type) {
|
||||
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
|
||||
ev.value.integer.value[id] = !!value;
|
||||
break;
|
||||
|
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER:
|
||||
ev.value.integer.value[id] = value;
|
||||
break;
|
||||
|
||||
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
|
||||
ev.value.enumerated.item[id] = value;
|
||||
break;
|
||||
|
||||
case SNDRV_CTL_ELEM_TYPE_BYTES:
|
||||
ev.value.bytes.data[id] = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
|
||||
}
|
||||
|
||||
int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
|
||||
{
|
||||
struct snd_ctl_elem_value ev;
|
||||
size_t size;
|
||||
void *dest;
|
||||
size_t total_count;
|
||||
|
||||
if ((!ctl) || !count || !array)
|
||||
return -EINVAL;
|
||||
|
||||
total_count = ctl->info->count;
|
||||
|
||||
if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
|
||||
mixer_ctl_is_access_tlv_rw(ctl)) {
|
||||
/* Additional two words is for the TLV header */
|
||||
total_count += TLV_HEADER_SIZE;
|
||||
}
|
||||
|
||||
if (count > total_count)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.id.numid = ctl->info->id.numid;
|
||||
|
||||
switch (ctl->info->type) {
|
||||
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
|
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER:
|
||||
size = sizeof(ev.value.integer.value[0]);
|
||||
dest = ev.value.integer.value;
|
||||
break;
|
||||
|
||||
case SNDRV_CTL_ELEM_TYPE_BYTES:
|
||||
/* check if this is new bytes TLV */
|
||||
if (mixer_ctl_is_access_tlv_rw(ctl)) {
|
||||
struct snd_ctl_tlv *tlv;
|
||||
int ret = 0;
|
||||
if (count > SIZE_MAX - sizeof(*tlv))
|
||||
return -EINVAL;
|
||||
tlv = calloc(1, sizeof(*tlv) + count);
|
||||
if (!tlv)
|
||||
return -ENOMEM;
|
||||
tlv->numid = ctl->info->id.numid;
|
||||
tlv->length = count;
|
||||
memcpy(tlv->tlv, array, count);
|
||||
|
||||
ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
|
||||
free(tlv);
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
size = sizeof(ev.value.bytes.data[0]);
|
||||
dest = ev.value.bytes.data;
|
||||
}
|
||||
break;
|
||||
|
||||
case SNDRV_CTL_ELEM_TYPE_IEC958:
|
||||
size = sizeof(ev.value.iec958);
|
||||
dest = &ev.value.iec958;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(dest, array, size * count);
|
||||
|
||||
return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
|
||||
}
|
||||
|
||||
int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
|
||||
{
|
||||
if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
|
||||
return -EINVAL;
|
||||
|
||||
return ctl->info->value.integer.min;
|
||||
}
|
||||
|
||||
int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
|
||||
{
|
||||
if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
|
||||
return -EINVAL;
|
||||
|
||||
return ctl->info->value.integer.max;
|
||||
}
|
||||
|
||||
unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
|
||||
{
|
||||
if (!ctl)
|
||||
return 0;
|
||||
|
||||
return ctl->info->value.enumerated.items;
|
||||
}
|
||||
|
||||
const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
|
||||
unsigned int enum_id)
|
||||
{
|
||||
if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
|
||||
(enum_id >= ctl->info->value.enumerated.items))
|
||||
return NULL;
|
||||
|
||||
return (const char *)ctl->ename[enum_id];
|
||||
}
|
||||
|
||||
int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
|
||||
{
|
||||
unsigned int i, num_enums;
|
||||
struct snd_ctl_elem_value ev;
|
||||
int ret;
|
||||
|
||||
if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
|
||||
return -EINVAL;
|
||||
|
||||
num_enums = ctl->info->value.enumerated.items;
|
||||
for (i = 0; i < num_enums; i++) {
|
||||
if (!strcmp(string, ctl->ename[i])) {
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.value.enumerated.item[0] = i;
|
||||
ev.id.numid = ctl->info->id.numid;
|
||||
ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/** Subscribes for the mixer events.
|
||||
* @param mixer A mixer handle.
|
||||
* @param subscribe value indicating subscribe or unsubscribe for events
|
||||
* @returns On success, zero.
|
||||
* On failure, non-zero.
|
||||
* @ingroup libtinyalsa-mixer
|
||||
*/
|
||||
int mixer_subscribe_events(struct mixer *mixer, int subscribe)
|
||||
{
|
||||
if (ioctl(mixer->fd, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Wait for mixer events.
|
||||
* @param mixer A mixer handle.
|
||||
* @param timeout timout value
|
||||
* @returns On success, 1.
|
||||
* On failure, -errno.
|
||||
* On timeout, 0
|
||||
* @ingroup libtinyalsa-mixer
|
||||
*/
|
||||
int mixer_wait_event(struct mixer *mixer, int timeout)
|
||||
{
|
||||
struct pollfd pfd;
|
||||
|
||||
pfd.fd = mixer->fd;
|
||||
pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
|
||||
|
||||
for (;;) {
|
||||
int err;
|
||||
err = poll(&pfd, 1, timeout);
|
||||
if (err < 0)
|
||||
return -errno;
|
||||
if (!err)
|
||||
return 0;
|
||||
if (pfd.revents & (POLLERR | POLLNVAL))
|
||||
return -EIO;
|
||||
if (pfd.revents & (POLLIN | POLLOUT))
|
||||
return 1;
|
||||
}
|
||||
}
|
703
app/src/main/cpp/native-lib.cpp
Normal file
@ -0,0 +1,703 @@
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
#include <linux/sockios.h>
|
||||
#include <pthread.h>
|
||||
#include <__locale>
|
||||
#include "osic-manager.h"
|
||||
#include <android/log.h>
|
||||
#include <unistd.h>
|
||||
#include "sha1sum.h"
|
||||
#include "tinyalsa/asoundlib.h"
|
||||
|
||||
#define LOG_TAG "ManagerNative"
|
||||
#define LOGUNK(...) __android_log_print(ANDROID_LOG_UNKNOWN,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGDEF(...) __android_log_print(ANDROID_LOG_DEFAULT,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGF(...) __android_log_print(ANDROID_FATAL_ERROR,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGS(...) __android_log_print(ANDROID_SILENT_ERROR,LOG_TAG,__VA_ARGS__)
|
||||
|
||||
static JavaVM* g_jvm;
|
||||
static jobject g_clazz;
|
||||
static jmethodID g_javafn_NotifyInfo;
|
||||
|
||||
#define ACQUIRE_JNIENV(vm, envptr) \
|
||||
{ \
|
||||
int getEnvStat = vm->GetEnv((void **)&envptr, JNI_VERSION_1_6); \
|
||||
if (getEnvStat == JNI_EDETACHED) { \
|
||||
if (vm->AttachCurrentThread(&envptr, NULL) != 0) { \
|
||||
LOGE("Unable to acquire JNIENV"); \
|
||||
} \
|
||||
} else if (getEnvStat == JNI_OK) { \
|
||||
} \
|
||||
}
|
||||
|
||||
#define RELEASE_JNIENV(vm, envptr) \
|
||||
{ \
|
||||
int getEnvStat = vm->GetEnv((void **)&envptr, JNI_VERSION_1_6); \
|
||||
if (getEnvStat != JNI_EDETACHED) { \
|
||||
vm->DetachCurrentThread(); \
|
||||
} \
|
||||
}
|
||||
|
||||
int callNotifyInfo(int code, char* strValue) {
|
||||
if(g_javafn_NotifyInfo != NULL) {
|
||||
JNIEnv *env;
|
||||
ACQUIRE_JNIENV(g_jvm, env);
|
||||
jstring jstrResult = env->NewStringUTF(strValue);
|
||||
env->CallVoidMethod(g_clazz, g_javafn_NotifyInfo, code, jstrResult);
|
||||
env->DeleteLocalRef(jstrResult);
|
||||
RELEASE_JNIENV(g_jvm, env);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_kr_co_rito_osicmanager_MainActivity_stringFromJNI(
|
||||
JNIEnv* env,
|
||||
jobject /* this */) {
|
||||
char strBuffer[512] = {0, };
|
||||
pthread_t tid;
|
||||
|
||||
get_interface_info("eth0", SIOCGIFHWADDR, strBuffer);
|
||||
pthread_create(&tid, NULL, osic_start_thread, NULL);
|
||||
//std::string hello = "Hello from C++";
|
||||
std::string hello = strBuffer;
|
||||
return env->NewStringUTF(hello.c_str());
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_kr_co_rito_osicmanager_MainActivity_getIPAddress(
|
||||
JNIEnv* env,
|
||||
jobject /* this */) {
|
||||
char strBuffer[512] = {0, };
|
||||
get_interface_info("eth0", SIOCGIFADDR, strBuffer);
|
||||
std::string result = strBuffer;
|
||||
return env->NewStringUTF(result.c_str());
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_kr_co_rito_osicmanager_MainActivity_getNetmask(
|
||||
JNIEnv* env,
|
||||
jobject /* this */) {
|
||||
char strBuffer[512] = {0, };
|
||||
get_interface_info("eth0", SIOCGIFNETMASK, strBuffer);
|
||||
std::string result = strBuffer;
|
||||
return env->NewStringUTF(result.c_str());
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_kr_co_rito_osicmanager_MainActivity_getGateway(
|
||||
JNIEnv* env,
|
||||
jobject /* this */) {
|
||||
char strBuffer[512] = {0, };
|
||||
get_default_gw(strBuffer);
|
||||
std::string result = strBuffer;
|
||||
return env->NewStringUTF(result.c_str());
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_kr_co_rito_osicmanager_MainActivity_getServerAddress(
|
||||
JNIEnv* env,
|
||||
jobject /* this */) {
|
||||
char strBuffer[512] = {0, };
|
||||
get_server_addr(strBuffer);
|
||||
std::string result = strBuffer;
|
||||
return env->NewStringUTF(result.c_str());
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_kr_co_rito_osicmanager_MainActivity_getServerPort(
|
||||
JNIEnv* env,
|
||||
jobject /* this */) {
|
||||
return get_server_port();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_kr_co_rito_osicmanager_MainActivity_getConfigValue(
|
||||
JNIEnv* env,
|
||||
jobject thiz,
|
||||
jstring target) {
|
||||
char strBuffer[512] = {0, };
|
||||
const char *pType = env->GetStringUTFChars(target, 0);
|
||||
getConfigValue((char *)pType, strBuffer);
|
||||
env->ReleaseStringUTFChars(target, pType);
|
||||
std::string result = strBuffer;
|
||||
return env->NewStringUTF(result.c_str());
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_kr_co_rito_osicmanager_MainActivity_nativeInit(
|
||||
JNIEnv* env,
|
||||
jobject thiz,
|
||||
jstring version) {
|
||||
char strBuffer[512] = {0, };
|
||||
|
||||
const char *pVersion = env->GetStringUTFChars(version, 0);
|
||||
setVersionString(pVersion);
|
||||
env->ReleaseStringUTFChars(version, pVersion);
|
||||
|
||||
jclass cls = env->GetObjectClass(thiz);
|
||||
g_clazz = env->NewWeakGlobalRef(thiz);
|
||||
g_javafn_NotifyInfo = env->GetMethodID(cls, "nativeNotify", "(ILjava/lang/String;)V");
|
||||
env->GetJavaVM(&g_jvm);
|
||||
|
||||
pthread_t tid;
|
||||
pthread_create(&tid, NULL, osic_start_thread, NULL);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_kr_co_rito_osicmanager_MainActivity_terminateService(
|
||||
JNIEnv* env,
|
||||
jobject thiz) {
|
||||
quit_osic9_mode();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_kr_co_rito_osicmanager_MainActivity_doSync(
|
||||
JNIEnv* env,
|
||||
jobject thiz) {
|
||||
::sync();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_kr_co_rito_osicmanager_MainActivity_setState(
|
||||
JNIEnv* env,
|
||||
jobject thiz,
|
||||
jstring target) {
|
||||
const char *pTarget = env->GetStringUTFChars(target, 0);
|
||||
//setVersionString(pTarget);
|
||||
setStateTarget((char *)pTarget);
|
||||
env->ReleaseStringUTFChars(target, pTarget);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_kr_co_rito_osicmanager_SystemUtil_getIPAddress(
|
||||
JNIEnv* env,
|
||||
jclass /* this */) {
|
||||
char strBuffer[512] = {0, };
|
||||
get_interface_info("eth0", SIOCGIFADDR, strBuffer);
|
||||
std::string result = strBuffer;
|
||||
return env->NewStringUTF(result.c_str());
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_kr_co_rito_osicmanager_SystemUtil_getConfigValue(
|
||||
JNIEnv* env,
|
||||
jclass thiz,
|
||||
jstring target) {
|
||||
char strBuffer[512] = {0, };
|
||||
const char *pType = env->GetStringUTFChars(target, 0);
|
||||
getConfigValue((char *)pType, strBuffer);
|
||||
env->ReleaseStringUTFChars(target, pType);
|
||||
std::string result = strBuffer;
|
||||
return env->NewStringUTF(result.c_str());
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_kr_co_rito_osicmanager_SystemUtil_setConfigValue(
|
||||
JNIEnv* env,
|
||||
jclass thiz,
|
||||
jstring target,
|
||||
jstring value) {
|
||||
char strBuffer[512] = {0, };
|
||||
const char *pType = env->GetStringUTFChars(target, 0);
|
||||
const char *pValue = env->GetStringUTFChars(value, 0);
|
||||
sprintf(strBuffer, "ritosysc CONFIG-WRITE=[%s]%s", pType, pValue);
|
||||
env->ReleaseStringUTFChars(target, pType);
|
||||
env->ReleaseStringUTFChars(value, pValue);
|
||||
system(strBuffer);
|
||||
sprintf(strBuffer, "ritosysc CONFIG-SAVE");
|
||||
system(strBuffer);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_kr_co_rito_osicmanager_MainActivity_getFileCksum(
|
||||
JNIEnv* env,
|
||||
jobject thiz,
|
||||
jstring target) {
|
||||
char strBuffer[512] = {0, };
|
||||
const char *pPath = env->GetStringUTFChars(target, 0);
|
||||
rito_cksum((char *)pPath, strBuffer);
|
||||
env->ReleaseStringUTFChars(target, pPath);
|
||||
std::string result = strBuffer;
|
||||
return env->NewStringUTF(result.c_str());
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_kr_co_rito_osicmanager_MainActivity_sendSocketMessage(
|
||||
JNIEnv* env,
|
||||
jobject thiz,
|
||||
jstring target) {
|
||||
const char *pTarget = env->GetStringUTFChars(target, 0);
|
||||
sendSocketMessage((char *)pTarget);
|
||||
env->ReleaseStringUTFChars(target, pTarget);
|
||||
}
|
||||
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
#define GPIO_IN 0
|
||||
#define GPIO_OUT 1
|
||||
|
||||
#define GPIO_LOW 0
|
||||
#define GPIO_HIGH 1
|
||||
|
||||
static int GPIOExport(int pin)
|
||||
{
|
||||
#define BUFFER_MAX 5
|
||||
char buffer[BUFFER_MAX];
|
||||
ssize_t bytes_written;
|
||||
int fd;
|
||||
|
||||
fd = open("/sys/class/gpio/export", O_WRONLY);
|
||||
if (-1 == fd) {
|
||||
LOGW("Failed to open export for writing!");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin);
|
||||
write(fd, buffer, bytes_written);
|
||||
close(fd);
|
||||
|
||||
char target_order[256];
|
||||
sprintf(target_order, "ritosysc SHELL-ORDER=chmod 666 /sys/class/gpio/gpio%d/direction", pin);
|
||||
system(target_order);
|
||||
sprintf(target_order, "ritosysc SHELL-ORDER=chmod 666 /sys/class/gpio/gpio%d/value", pin);
|
||||
system(target_order);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int GPIOUnexport(int pin)
|
||||
{
|
||||
char buffer[BUFFER_MAX];
|
||||
ssize_t bytes_written;
|
||||
int fd;
|
||||
|
||||
fd = open("/sys/class/gpio/unexport", O_WRONLY);
|
||||
if (-1 == fd) {
|
||||
LOGW("Failed to open unexport for writing!");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin);
|
||||
write(fd, buffer, bytes_written);
|
||||
close(fd);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int GPIODirection(int pin, int dir)
|
||||
{
|
||||
static const char s_directions_str[] = "in\0out";
|
||||
|
||||
#define DIRECTION_MAX 64
|
||||
char path[DIRECTION_MAX];
|
||||
int fd;
|
||||
|
||||
snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/direction", pin);
|
||||
fd = open(path, O_WRONLY);
|
||||
if (-1 == fd) {
|
||||
LOGW("Failed to open gpio direction for writing!");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (-1 == write(fd, &s_directions_str[GPIO_IN == dir ? 0 : 3], GPIO_IN == dir ? 2 : 3)) {
|
||||
LOGW("Failed to set direction!");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return(0);
|
||||
}
|
||||
|
||||
#define VALUE_MAX 64
|
||||
static int GPIORead(int pin)
|
||||
{
|
||||
char path[VALUE_MAX];
|
||||
char value_str[3];
|
||||
int fd;
|
||||
|
||||
snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", pin);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (-1 == fd) {
|
||||
LOGW("Failed to open gpio value for reading!");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (-1 == read(fd, value_str, 3)) {
|
||||
LOGW("Failed to read value!");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return(atoi(value_str));
|
||||
}
|
||||
|
||||
static int GPIOWrite(int pin, int value)
|
||||
{
|
||||
static const char s_values_str[] = "01";
|
||||
|
||||
char path[VALUE_MAX];
|
||||
int fd;
|
||||
|
||||
snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", pin);
|
||||
fd = open(path, O_WRONLY);
|
||||
if (-1 == fd) {
|
||||
LOGW("Failed to open gpio value for writing!\n");
|
||||
return(-1);
|
||||
}
|
||||
if (1 != write(fd, &s_values_str[GPIO_LOW == value ? 0 : 1], 1)) {
|
||||
LOGW("Failed to write value!\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return(0);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_kr_co_rito_osicmanager_SystemUtil_getPinValue(
|
||||
JNIEnv* env,
|
||||
jclass /* this */,
|
||||
jint pinNo) {
|
||||
int ret;
|
||||
ret = GPIORead(pinNo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_kr_co_rito_osicmanager_SystemUtil_setPinValue(
|
||||
JNIEnv* env,
|
||||
jclass /* this */,
|
||||
jint pinNo,
|
||||
jint value) {
|
||||
int ret;
|
||||
ret = GPIOWrite(pinNo, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_kr_co_rito_osicmanager_SystemUtil_pinSetup(
|
||||
JNIEnv* env,
|
||||
jclass /* this */,
|
||||
jint pinNo,
|
||||
jint pinDirection) {
|
||||
int ret;
|
||||
|
||||
ret = GPIOExport(pinNo);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
if(pinDirection == GPIO_IN) {
|
||||
GPIODirection(pinNo, GPIO_OUT);
|
||||
GPIOWrite(pinNo, GPIO_HIGH);
|
||||
} else {
|
||||
ret = GPIODirection(pinNo, pinDirection);
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define ID_RIFF 0x46464952
|
||||
#define ID_WAVE 0x45564157
|
||||
#define ID_FMT 0x20746d66
|
||||
#define ID_DATA 0x61746164
|
||||
|
||||
struct riff_wave_header {
|
||||
uint32_t riff_id;
|
||||
uint32_t riff_sz;
|
||||
uint32_t wave_id;
|
||||
};
|
||||
|
||||
struct chunk_header {
|
||||
uint32_t id;
|
||||
uint32_t sz;
|
||||
};
|
||||
|
||||
struct chunk_fmt {
|
||||
uint16_t audio_format;
|
||||
uint16_t num_channels;
|
||||
uint32_t sample_rate;
|
||||
uint32_t byte_rate;
|
||||
uint16_t block_align;
|
||||
uint16_t bits_per_sample;
|
||||
};
|
||||
|
||||
int check_param(struct pcm_params *params, unsigned int param, unsigned int value,
|
||||
char *param_name, char *param_unit)
|
||||
{
|
||||
unsigned int min;
|
||||
unsigned int max;
|
||||
int is_within_bounds = 1;
|
||||
|
||||
min = pcm_params_get_min(params, static_cast<pcm_param>(param));
|
||||
if (value < min) {
|
||||
fprintf(stderr, "%s is %u%s, device only supports >= %u%s\n", param_name, value,
|
||||
param_unit, min, param_unit);
|
||||
is_within_bounds = 0;
|
||||
}
|
||||
|
||||
max = pcm_params_get_max(params, static_cast<pcm_param>(param));
|
||||
if (value > max) {
|
||||
fprintf(stderr, "%s is %u%s, device only supports <= %u%s\n", param_name, value,
|
||||
param_unit, max, param_unit);
|
||||
is_within_bounds = 0;
|
||||
}
|
||||
|
||||
return is_within_bounds;
|
||||
}
|
||||
|
||||
|
||||
int sample_is_playable(unsigned int card, unsigned int device, unsigned int channels,
|
||||
unsigned int rate, unsigned int bits, unsigned int period_size,
|
||||
unsigned int period_count)
|
||||
{
|
||||
struct pcm_params *params;
|
||||
int can_play;
|
||||
|
||||
params = pcm_params_get(card, device, PCM_OUT);
|
||||
if (params == NULL) {
|
||||
fprintf(stderr, "Unable to open PCM device %u.\n", device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
can_play = check_param(params, PCM_PARAM_RATE, rate, (char*)"Sample rate", (char*)"Hz");
|
||||
can_play &= check_param(params, PCM_PARAM_CHANNELS, channels, (char*)"Sample", (char*)" channels");
|
||||
can_play &= check_param(params, PCM_PARAM_SAMPLE_BITS, bits, (char*)"Bitrate", (char*)" bits");
|
||||
can_play &= check_param(params, PCM_PARAM_PERIOD_SIZE, period_size, (char*)"Period size", (char*)" frames");
|
||||
can_play &= check_param(params, PCM_PARAM_PERIODS, period_count, (char*)"Period count", (char*)" periods");
|
||||
|
||||
pcm_params_free(params);
|
||||
|
||||
return can_play;
|
||||
}
|
||||
|
||||
void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
|
||||
unsigned int rate, unsigned int bits, unsigned int period_size,
|
||||
unsigned int period_count)
|
||||
{
|
||||
struct pcm_config config;
|
||||
struct pcm *pcm;
|
||||
char *buffer;
|
||||
int size;
|
||||
int num_read;
|
||||
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.channels = channels;
|
||||
config.rate = rate;
|
||||
config.period_size = period_size;
|
||||
config.period_count = period_count;
|
||||
if (bits == 32)
|
||||
config.format = PCM_FORMAT_S32_LE;
|
||||
else if (bits == 24)
|
||||
config.format = PCM_FORMAT_S24_3LE;
|
||||
else if (bits == 16)
|
||||
config.format = PCM_FORMAT_S16_LE;
|
||||
config.start_threshold = 0;
|
||||
config.stop_threshold = 0;
|
||||
config.silence_threshold = 0;
|
||||
|
||||
if (!sample_is_playable(card, device, channels, rate, bits, period_size, period_count)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pcm = pcm_open(card, device, PCM_OUT, &config);
|
||||
if (!pcm || !pcm_is_ready(pcm)) {
|
||||
fprintf(stderr, "Unable to open PCM device %u (%s)\n",
|
||||
device, pcm_get_error(pcm));
|
||||
return;
|
||||
}
|
||||
|
||||
size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
|
||||
buffer = (char *)malloc(size);
|
||||
if (!buffer) {
|
||||
fprintf(stderr, "Unable to allocate %d bytes\n", size);
|
||||
free(buffer);
|
||||
pcm_close(pcm);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
|
||||
|
||||
do {
|
||||
num_read = fread(buffer, 1, size, file);
|
||||
if (num_read > 0) {
|
||||
if (pcm_write(pcm, buffer, num_read)) {
|
||||
fprintf(stderr, "Error playing sample\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (num_read > 0);
|
||||
|
||||
free(buffer);
|
||||
pcm_close(pcm);
|
||||
}
|
||||
|
||||
int do_tinyplay(char *filename, unsigned int device_id, unsigned int card_id)
|
||||
{
|
||||
FILE *file;
|
||||
struct riff_wave_header riff_wave_header;
|
||||
struct chunk_header chunk_header;
|
||||
struct chunk_fmt chunk_fmt;
|
||||
unsigned int device = 0;
|
||||
unsigned int card = 0;
|
||||
unsigned int period_size = 1024;
|
||||
unsigned int period_count = 4;
|
||||
int more_chunks = 1;
|
||||
|
||||
LOGW("RITO TRACE[%d]", __LINE__);
|
||||
file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
LOGW("Unable to open file '%s'\n", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOGW("RITO TRACE[%d]", __LINE__);
|
||||
fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
|
||||
if ((riff_wave_header.riff_id != ID_RIFF) ||
|
||||
(riff_wave_header.wave_id != ID_WAVE)) {
|
||||
LOGW("Error: '%s' is not a riff/wave file\n", filename);
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOGW("RITO TRACE[%d]", __LINE__);
|
||||
do {
|
||||
fread(&chunk_header, sizeof(chunk_header), 1, file);
|
||||
|
||||
switch (chunk_header.id) {
|
||||
case ID_FMT:
|
||||
fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
|
||||
/* If the format header is larger, skip the rest */
|
||||
if (chunk_header.sz > sizeof(chunk_fmt))
|
||||
fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
|
||||
break;
|
||||
case ID_DATA:
|
||||
/* Stop looking for chunks */
|
||||
more_chunks = 0;
|
||||
break;
|
||||
default:
|
||||
/* Unknown chunk, skip bytes */
|
||||
fseek(file, chunk_header.sz, SEEK_CUR);
|
||||
}
|
||||
} while (more_chunks);
|
||||
|
||||
LOGW("RITO TRACE[%d]", __LINE__);
|
||||
device = device_id;
|
||||
card = card_id;
|
||||
|
||||
play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate,
|
||||
chunk_fmt.bits_per_sample, period_size, period_count);
|
||||
|
||||
LOGW("RITO TRACE[%d]", __LINE__);
|
||||
fclose(file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct pcm *gp_pcm = NULL;
|
||||
int aout_setup(unsigned int card, unsigned int device, unsigned int channels,
|
||||
unsigned int rate, unsigned int bits, unsigned int period_size,
|
||||
unsigned int period_count)
|
||||
{
|
||||
struct pcm_config config;
|
||||
struct pcm *pcm;
|
||||
char *buffer;
|
||||
int size;
|
||||
int num_read;
|
||||
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.channels = channels;
|
||||
config.rate = rate;
|
||||
config.period_size = period_size;
|
||||
config.period_count = period_count;
|
||||
if (bits == 32)
|
||||
config.format = PCM_FORMAT_S32_LE;
|
||||
else if (bits == 24)
|
||||
config.format = PCM_FORMAT_S24_3LE;
|
||||
else if (bits == 16)
|
||||
config.format = PCM_FORMAT_S16_LE;
|
||||
config.start_threshold = 0;
|
||||
config.stop_threshold = 0;
|
||||
config.silence_threshold = 0;
|
||||
|
||||
if (!sample_is_playable(card, device, channels, rate, bits, period_size, period_count)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pcm = pcm_open(card, device, PCM_OUT, &config);
|
||||
if (!pcm || !pcm_is_ready(pcm)) {
|
||||
fprintf(stderr, "Unable to open PCM device %u (%s)\n",
|
||||
device, pcm_get_error(pcm));
|
||||
return -2;
|
||||
}
|
||||
|
||||
size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
|
||||
buffer = (char *)malloc(size);
|
||||
if (!buffer) {
|
||||
fprintf(stderr, "Unable to allocate %d bytes\n", size);
|
||||
free(buffer);
|
||||
pcm_close(pcm);
|
||||
return -3;
|
||||
}
|
||||
|
||||
printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
|
||||
|
||||
gp_pcm = pcm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void aout_close() {
|
||||
if(gp_pcm) {
|
||||
pcm_close(gp_pcm);
|
||||
gp_pcm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_kr_co_rito_osicmanager_SystemUtil_playWaveFile(
|
||||
JNIEnv* env,
|
||||
jclass /* this */,
|
||||
jstring filePath,
|
||||
jint device_id) {
|
||||
int ret;
|
||||
const char *pPath = env->GetStringUTFChars(filePath, 0);
|
||||
ret = do_tinyplay((char*)pPath, device_id, 0);
|
||||
env->ReleaseStringUTFChars(filePath, pPath);
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_kr_co_rito_osicmanager_SystemUtil_setupAudioOut(
|
||||
JNIEnv* env,
|
||||
jclass /* this */,
|
||||
jint device_id) {
|
||||
int ret = aout_setup(0, device_id, 2, 44100, 16, 1024, 4);
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_kr_co_rito_osicmanager_SystemUtil_writeAudioOut(
|
||||
JNIEnv* env,
|
||||
jclass /* this */,
|
||||
jbyteArray buffer,
|
||||
jint size) {
|
||||
jbyte *pBuffer;
|
||||
pBuffer = env->GetByteArrayElements(buffer, NULL);
|
||||
pcm_write(gp_pcm, pBuffer, size);
|
||||
env->ReleaseByteArrayElements(buffer, pBuffer, 0);
|
||||
return size;
|
||||
}
|
||||
|
2554
app/src/main/cpp/osic-manager.cpp
Normal file
28
app/src/main/cpp/osic-manager.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef OSIC_MANAGER_H
|
||||
#define OSIC_MANAGER_H
|
||||
|
||||
static int MANAGER_NOTIFY_ORDER_START_PLAYBACK = 1;
|
||||
static int MANAGER_NOTIFY_ORDER_STOP_PLAYBACK = 2;
|
||||
static int MANAGER_NOTIFY_ORDER_START_TICKER = 3;
|
||||
static int MANAGER_NOTIFY_ORDER_START_TTS = 4;
|
||||
static int MANAGER_NOTIFY_ORDER_CEC_CONTROL = 5;
|
||||
static int MANAGER_NOTIFY_ORDER_JSON_ORDER = 6;
|
||||
static int MANAGER_NOTIFY_ORDER_NOTIFY_SCREEN_ORDER = 7;
|
||||
static int MANAGER_NOTIFY_ORDER_EVENT_MESSAGE = 8;
|
||||
static int MANAGER_NOTIFY_ORDER_START_AUDIO = 9;
|
||||
static int MANAGER_NOTIFY_ORDER_STOP_AUDIO = 10;
|
||||
|
||||
int callNotifyInfo(int code, char* strValue);
|
||||
int get_interface_info( const char *eth, int infotype, char *rtnval );
|
||||
int get_default_gw(char *pBuffer);
|
||||
int get_server_addr(char *pBuffer);
|
||||
int get_server_port();
|
||||
int getConfigValue(char *pType, char *pResult);
|
||||
void quit_osic9_mode();
|
||||
void *osic_start_thread(void *pArg);
|
||||
void setVersionString(const char *pVersion);
|
||||
void setStateTarget(char *pTarget);
|
||||
void sendSocketMessage(char *pTarget);
|
||||
|
||||
|
||||
#endif
|
1369
app/src/main/cpp/pcm.c
Normal file
365
app/src/main/cpp/sha1sum.cpp
Normal file
@ -0,0 +1,365 @@
|
||||
//
|
||||
// Created by 서인석 on 2020-10-22.
|
||||
//
|
||||
|
||||
/* sha1sum.c - print SHA-1 Message-Digest Algorithm
|
||||
* Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2004 g10 Code GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/* SHA-1 coden take from gnupg 1.3.92.
|
||||
|
||||
Note, that this is a simple tool to be used for MS Windows.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#undef BIG_ENDIAN_HOST
|
||||
typedef unsigned int u32;
|
||||
|
||||
/****************
|
||||
* Rotate a 32 bit integer by n bytes
|
||||
*/
|
||||
#if defined(__GNUC__) && defined(__i386__)
|
||||
static inline u32
|
||||
rol( u32 x, int n)
|
||||
{
|
||||
__asm__("roll %%cl,%0"
|
||||
:"=r" (x)
|
||||
:"0" (x),"c" (n));
|
||||
return x;
|
||||
}
|
||||
#else
|
||||
#define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) )
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
u32 h0,h1,h2,h3,h4;
|
||||
u32 nblocks;
|
||||
unsigned char buf[64];
|
||||
int count;
|
||||
} SHA1_CONTEXT;
|
||||
|
||||
|
||||
|
||||
void
|
||||
sha1_init( SHA1_CONTEXT *hd )
|
||||
{
|
||||
hd->h0 = 0x67452301;
|
||||
hd->h1 = 0xefcdab89;
|
||||
hd->h2 = 0x98badcfe;
|
||||
hd->h3 = 0x10325476;
|
||||
hd->h4 = 0xc3d2e1f0;
|
||||
hd->nblocks = 0;
|
||||
hd->count = 0;
|
||||
}
|
||||
|
||||
|
||||
/****************
|
||||
* Transform the message X which consists of 16 32-bit-words
|
||||
*/
|
||||
static void
|
||||
transform( SHA1_CONTEXT *hd, unsigned char *data )
|
||||
{
|
||||
u32 a,b,c,d,e,tm;
|
||||
u32 x[16];
|
||||
|
||||
/* get values from the chaining vars */
|
||||
a = hd->h0;
|
||||
b = hd->h1;
|
||||
c = hd->h2;
|
||||
d = hd->h3;
|
||||
e = hd->h4;
|
||||
|
||||
#ifdef BIG_ENDIAN_HOST
|
||||
memcpy( x, data, 64 );
|
||||
#else
|
||||
{ int i;
|
||||
unsigned char *p2;
|
||||
for(i=0, p2=(unsigned char*)x; i < 16; i++, p2 += 4 ) {
|
||||
p2[3] = *data++;
|
||||
p2[2] = *data++;
|
||||
p2[1] = *data++;
|
||||
p2[0] = *data++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#define K1 0x5A827999L
|
||||
#define K2 0x6ED9EBA1L
|
||||
#define K3 0x8F1BBCDCL
|
||||
#define K4 0xCA62C1D6L
|
||||
#define F1(x,y,z) ( z ^ ( x & ( y ^ z ) ) )
|
||||
#define F2(x,y,z) ( x ^ y ^ z )
|
||||
#define F3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) )
|
||||
#define F4(x,y,z) ( x ^ y ^ z )
|
||||
|
||||
|
||||
#define M(i) ( tm = x[i&0x0f] ^ x[(i-14)&0x0f] \
|
||||
^ x[(i-8)&0x0f] ^ x[(i-3)&0x0f] \
|
||||
, (x[i&0x0f] = rol(tm,1)) )
|
||||
|
||||
#define R(a,b,c,d,e,f,k,m) do { e += rol( a, 5 ) \
|
||||
+ f( b, c, d ) \
|
||||
+ k \
|
||||
+ m; \
|
||||
b = rol( b, 30 ); \
|
||||
} while(0)
|
||||
R( a, b, c, d, e, F1, K1, x[ 0] );
|
||||
R( e, a, b, c, d, F1, K1, x[ 1] );
|
||||
R( d, e, a, b, c, F1, K1, x[ 2] );
|
||||
R( c, d, e, a, b, F1, K1, x[ 3] );
|
||||
R( b, c, d, e, a, F1, K1, x[ 4] );
|
||||
R( a, b, c, d, e, F1, K1, x[ 5] );
|
||||
R( e, a, b, c, d, F1, K1, x[ 6] );
|
||||
R( d, e, a, b, c, F1, K1, x[ 7] );
|
||||
R( c, d, e, a, b, F1, K1, x[ 8] );
|
||||
R( b, c, d, e, a, F1, K1, x[ 9] );
|
||||
R( a, b, c, d, e, F1, K1, x[10] );
|
||||
R( e, a, b, c, d, F1, K1, x[11] );
|
||||
R( d, e, a, b, c, F1, K1, x[12] );
|
||||
R( c, d, e, a, b, F1, K1, x[13] );
|
||||
R( b, c, d, e, a, F1, K1, x[14] );
|
||||
R( a, b, c, d, e, F1, K1, x[15] );
|
||||
R( e, a, b, c, d, F1, K1, M(16) );
|
||||
R( d, e, a, b, c, F1, K1, M(17) );
|
||||
R( c, d, e, a, b, F1, K1, M(18) );
|
||||
R( b, c, d, e, a, F1, K1, M(19) );
|
||||
R( a, b, c, d, e, F2, K2, M(20) );
|
||||
R( e, a, b, c, d, F2, K2, M(21) );
|
||||
R( d, e, a, b, c, F2, K2, M(22) );
|
||||
R( c, d, e, a, b, F2, K2, M(23) );
|
||||
R( b, c, d, e, a, F2, K2, M(24) );
|
||||
R( a, b, c, d, e, F2, K2, M(25) );
|
||||
R( e, a, b, c, d, F2, K2, M(26) );
|
||||
R( d, e, a, b, c, F2, K2, M(27) );
|
||||
R( c, d, e, a, b, F2, K2, M(28) );
|
||||
R( b, c, d, e, a, F2, K2, M(29) );
|
||||
R( a, b, c, d, e, F2, K2, M(30) );
|
||||
R( e, a, b, c, d, F2, K2, M(31) );
|
||||
R( d, e, a, b, c, F2, K2, M(32) );
|
||||
R( c, d, e, a, b, F2, K2, M(33) );
|
||||
R( b, c, d, e, a, F2, K2, M(34) );
|
||||
R( a, b, c, d, e, F2, K2, M(35) );
|
||||
R( e, a, b, c, d, F2, K2, M(36) );
|
||||
R( d, e, a, b, c, F2, K2, M(37) );
|
||||
R( c, d, e, a, b, F2, K2, M(38) );
|
||||
R( b, c, d, e, a, F2, K2, M(39) );
|
||||
R( a, b, c, d, e, F3, K3, M(40) );
|
||||
R( e, a, b, c, d, F3, K3, M(41) );
|
||||
R( d, e, a, b, c, F3, K3, M(42) );
|
||||
R( c, d, e, a, b, F3, K3, M(43) );
|
||||
R( b, c, d, e, a, F3, K3, M(44) );
|
||||
R( a, b, c, d, e, F3, K3, M(45) );
|
||||
R( e, a, b, c, d, F3, K3, M(46) );
|
||||
R( d, e, a, b, c, F3, K3, M(47) );
|
||||
R( c, d, e, a, b, F3, K3, M(48) );
|
||||
R( b, c, d, e, a, F3, K3, M(49) );
|
||||
R( a, b, c, d, e, F3, K3, M(50) );
|
||||
R( e, a, b, c, d, F3, K3, M(51) );
|
||||
R( d, e, a, b, c, F3, K3, M(52) );
|
||||
R( c, d, e, a, b, F3, K3, M(53) );
|
||||
R( b, c, d, e, a, F3, K3, M(54) );
|
||||
R( a, b, c, d, e, F3, K3, M(55) );
|
||||
R( e, a, b, c, d, F3, K3, M(56) );
|
||||
R( d, e, a, b, c, F3, K3, M(57) );
|
||||
R( c, d, e, a, b, F3, K3, M(58) );
|
||||
R( b, c, d, e, a, F3, K3, M(59) );
|
||||
R( a, b, c, d, e, F4, K4, M(60) );
|
||||
R( e, a, b, c, d, F4, K4, M(61) );
|
||||
R( d, e, a, b, c, F4, K4, M(62) );
|
||||
R( c, d, e, a, b, F4, K4, M(63) );
|
||||
R( b, c, d, e, a, F4, K4, M(64) );
|
||||
R( a, b, c, d, e, F4, K4, M(65) );
|
||||
R( e, a, b, c, d, F4, K4, M(66) );
|
||||
R( d, e, a, b, c, F4, K4, M(67) );
|
||||
R( c, d, e, a, b, F4, K4, M(68) );
|
||||
R( b, c, d, e, a, F4, K4, M(69) );
|
||||
R( a, b, c, d, e, F4, K4, M(70) );
|
||||
R( e, a, b, c, d, F4, K4, M(71) );
|
||||
R( d, e, a, b, c, F4, K4, M(72) );
|
||||
R( c, d, e, a, b, F4, K4, M(73) );
|
||||
R( b, c, d, e, a, F4, K4, M(74) );
|
||||
R( a, b, c, d, e, F4, K4, M(75) );
|
||||
R( e, a, b, c, d, F4, K4, M(76) );
|
||||
R( d, e, a, b, c, F4, K4, M(77) );
|
||||
R( c, d, e, a, b, F4, K4, M(78) );
|
||||
R( b, c, d, e, a, F4, K4, M(79) );
|
||||
|
||||
/* Update chaining vars */
|
||||
hd->h0 += a;
|
||||
hd->h1 += b;
|
||||
hd->h2 += c;
|
||||
hd->h3 += d;
|
||||
hd->h4 += e;
|
||||
}
|
||||
|
||||
|
||||
/* Update the message digest with the contents
|
||||
* of INBUF with length INLEN.
|
||||
*/
|
||||
static void
|
||||
sha1_write( SHA1_CONTEXT *hd, unsigned char *inbuf, size_t inlen)
|
||||
{
|
||||
if( hd->count == 64 ) { /* flush the buffer */
|
||||
transform( hd, hd->buf );
|
||||
hd->count = 0;
|
||||
hd->nblocks++;
|
||||
}
|
||||
if( !inbuf )
|
||||
return;
|
||||
if( hd->count ) {
|
||||
for( ; inlen && hd->count < 64; inlen-- )
|
||||
hd->buf[hd->count++] = *inbuf++;
|
||||
sha1_write( hd, NULL, 0 );
|
||||
if( !inlen )
|
||||
return;
|
||||
}
|
||||
|
||||
while( inlen >= 64 ) {
|
||||
transform( hd, inbuf );
|
||||
hd->count = 0;
|
||||
hd->nblocks++;
|
||||
inlen -= 64;
|
||||
inbuf += 64;
|
||||
}
|
||||
for( ; inlen && hd->count < 64; inlen-- )
|
||||
hd->buf[hd->count++] = *inbuf++;
|
||||
}
|
||||
|
||||
|
||||
/* The routine final terminates the computation and
|
||||
* returns the digest.
|
||||
* The handle is prepared for a new cycle, but adding bytes to the
|
||||
* handle will the destroy the returned buffer.
|
||||
* Returns: 20 bytes representing the digest.
|
||||
*/
|
||||
|
||||
static void
|
||||
sha1_final(SHA1_CONTEXT *hd)
|
||||
{
|
||||
u32 t, msb, lsb;
|
||||
unsigned char *p;
|
||||
|
||||
sha1_write(hd, NULL, 0); /* flush */;
|
||||
|
||||
t = hd->nblocks;
|
||||
/* multiply by 64 to make a byte count */
|
||||
lsb = t << 6;
|
||||
msb = t >> 26;
|
||||
/* add the count */
|
||||
t = lsb;
|
||||
if( (lsb += hd->count) < t )
|
||||
msb++;
|
||||
/* multiply by 8 to make a bit count */
|
||||
t = lsb;
|
||||
lsb <<= 3;
|
||||
msb <<= 3;
|
||||
msb |= t >> 29;
|
||||
|
||||
if( hd->count < 56 ) { /* enough room */
|
||||
hd->buf[hd->count++] = 0x80; /* pad */
|
||||
while( hd->count < 56 )
|
||||
hd->buf[hd->count++] = 0; /* pad */
|
||||
}
|
||||
else { /* need one extra block */
|
||||
hd->buf[hd->count++] = 0x80; /* pad character */
|
||||
while( hd->count < 64 )
|
||||
hd->buf[hd->count++] = 0;
|
||||
sha1_write(hd, NULL, 0); /* flush */;
|
||||
memset(hd->buf, 0, 56 ); /* fill next block with zeroes */
|
||||
}
|
||||
/* append the 64 bit count */
|
||||
hd->buf[56] = msb >> 24;
|
||||
hd->buf[57] = msb >> 16;
|
||||
hd->buf[58] = msb >> 8;
|
||||
hd->buf[59] = msb ;
|
||||
hd->buf[60] = lsb >> 24;
|
||||
hd->buf[61] = lsb >> 16;
|
||||
hd->buf[62] = lsb >> 8;
|
||||
hd->buf[63] = lsb ;
|
||||
transform( hd, hd->buf );
|
||||
|
||||
p = hd->buf;
|
||||
#ifdef BIG_ENDIAN_HOST
|
||||
#define X(a) do { *(u32*)p = hd->h##a ; p += 4; } while(0)
|
||||
#else /* little endian */
|
||||
#define X(a) do { *p++ = hd->h##a >> 24; *p++ = hd->h##a >> 16; \
|
||||
*p++ = hd->h##a >> 8; *p++ = hd->h##a; } while(0)
|
||||
#endif
|
||||
X(0);
|
||||
X(1);
|
||||
X(2);
|
||||
X(3);
|
||||
X(4);
|
||||
#undef X
|
||||
}
|
||||
|
||||
|
||||
int rito_cksum (char *pFilePath, char *pResult)
|
||||
{
|
||||
FILE *fp;
|
||||
char buffer[4096];
|
||||
size_t n;
|
||||
size_t file_size = 0;
|
||||
size_t jump = 0;
|
||||
SHA1_CONTEXT ctx;
|
||||
int i;
|
||||
|
||||
fp = fopen(pFilePath, "rb");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "can't open `%s': %s\n", pFilePath, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
file_size = ftell(fp);
|
||||
jump = file_size / (4 * 1024 * 1024) * sizeof(buffer);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
sha1_init(&ctx);
|
||||
|
||||
while ((n = fread(buffer, 1, sizeof buffer, fp))) {
|
||||
sha1_write(&ctx, reinterpret_cast<unsigned char *>(buffer), n);
|
||||
fseek(fp, jump, SEEK_CUR);
|
||||
}
|
||||
if (ferror(fp)) {
|
||||
fprintf(stderr, "error reading `%s': %s\n", pFilePath, strerror(errno));
|
||||
fclose(fp);
|
||||
return -2;
|
||||
}
|
||||
sha1_final(&ctx);
|
||||
fclose(fp);
|
||||
|
||||
if (pResult) {
|
||||
for (i = 0; i < 20; i++)
|
||||
sprintf(pResult + strlen(pResult), "%02x", ctx.buf[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
compile-command: "cc -Wall -g -o sha1sum sha1sum.c"
|
||||
End:
|
||||
*/
|
10
app/src/main/cpp/sha1sum.h
Normal file
@ -0,0 +1,10 @@
|
||||
//
|
||||
// Created by 서인석 on 2020-10-22.
|
||||
//
|
||||
|
||||
#ifndef OSICMANAGER_SHA1SUM_H
|
||||
#define OSICMANAGER_SHA1SUM_H
|
||||
|
||||
int rito_cksum (char *pFilePath, char *pResult);
|
||||
|
||||
#endif //OSICMANAGER_SHA1SUM_H
|
325
app/src/main/cpp/tinyalsa/asoundlib.h
Normal file
@ -0,0 +1,325 @@
|
||||
/* asoundlib.h
|
||||
**
|
||||
** Copyright 2011, The Android Open Source Project
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** * Neither the name of The Android Open Source Project nor the names of
|
||||
** its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
|
||||
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
|
||||
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
** DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ASOUNDLIB_H
|
||||
#define ASOUNDLIB_H
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* PCM API
|
||||
*/
|
||||
|
||||
struct pcm;
|
||||
|
||||
#define PCM_OUT 0x00000000
|
||||
#define PCM_IN 0x10000000
|
||||
#define PCM_MMAP 0x00000001
|
||||
#define PCM_NOIRQ 0x00000002
|
||||
#define PCM_NORESTART 0x00000004 /* PCM_NORESTART - when set, calls to
|
||||
* pcm_write for a playback stream will not
|
||||
* attempt to restart the stream in the case
|
||||
* of an underflow, but will return -EPIPE
|
||||
* instead. After the first -EPIPE error, the
|
||||
* stream is considered to be stopped, and a
|
||||
* second call to pcm_write will attempt to
|
||||
* restart the stream.
|
||||
*/
|
||||
#define PCM_MONOTONIC 0x00000008 /* see pcm_get_htimestamp */
|
||||
|
||||
/* PCM runtime states */
|
||||
#define PCM_STATE_OPEN 0
|
||||
#define PCM_STATE_SETUP 1
|
||||
#define PCM_STATE_PREPARED 2
|
||||
#define PCM_STATE_RUNNING 3
|
||||
#define PCM_STATE_XRUN 4
|
||||
#define PCM_STATE_DRAINING 5
|
||||
#define PCM_STATE_PAUSED 6
|
||||
#define PCM_STATE_SUSPENDED 7
|
||||
#define PCM_STATE_DISCONNECTED 8
|
||||
|
||||
/* TLV header size*/
|
||||
#define TLV_HEADER_SIZE (2 * sizeof(unsigned int))
|
||||
|
||||
/* Bit formats */
|
||||
enum pcm_format {
|
||||
PCM_FORMAT_INVALID = -1,
|
||||
PCM_FORMAT_S16_LE = 0, /* 16-bit signed */
|
||||
PCM_FORMAT_S32_LE, /* 32-bit signed */
|
||||
PCM_FORMAT_S8, /* 8-bit signed */
|
||||
PCM_FORMAT_S24_LE, /* 24-bits in 4-bytes */
|
||||
PCM_FORMAT_S24_3LE, /* 24-bits in 3-bytes */
|
||||
|
||||
PCM_FORMAT_MAX,
|
||||
};
|
||||
|
||||
/* Bitmask has 256 bits (32 bytes) in asound.h */
|
||||
struct pcm_mask {
|
||||
unsigned int bits[32 / sizeof(unsigned int)];
|
||||
};
|
||||
|
||||
/* Configuration for a stream */
|
||||
struct pcm_config {
|
||||
unsigned int channels;
|
||||
unsigned int rate;
|
||||
unsigned int period_size;
|
||||
unsigned int period_count;
|
||||
enum pcm_format format;
|
||||
|
||||
/* Values to use for the ALSA start, stop and silence thresholds, and
|
||||
* silence size. Setting any one of these values to 0 will cause the
|
||||
* default tinyalsa values to be used instead.
|
||||
* Tinyalsa defaults are as follows.
|
||||
*
|
||||
* start_threshold : period_count * period_size
|
||||
* stop_threshold : period_count * period_size
|
||||
* silence_threshold : 0
|
||||
* silence_size : 0
|
||||
*/
|
||||
unsigned int start_threshold;
|
||||
unsigned int stop_threshold;
|
||||
unsigned int silence_threshold;
|
||||
unsigned int silence_size;
|
||||
|
||||
/* Minimum number of frames available before pcm_mmap_write() will actually
|
||||
* write into the kernel buffer. Only used if the stream is opened in mmap mode
|
||||
* (pcm_open() called with PCM_MMAP flag set). Use 0 for default.
|
||||
*/
|
||||
int avail_min;
|
||||
int flag;
|
||||
};
|
||||
|
||||
/* PCM parameters */
|
||||
enum pcm_param
|
||||
{
|
||||
/* mask parameters */
|
||||
PCM_PARAM_ACCESS,
|
||||
PCM_PARAM_FORMAT,
|
||||
PCM_PARAM_SUBFORMAT,
|
||||
/* interval parameters */
|
||||
PCM_PARAM_SAMPLE_BITS,
|
||||
PCM_PARAM_FRAME_BITS,
|
||||
PCM_PARAM_CHANNELS,
|
||||
PCM_PARAM_RATE,
|
||||
PCM_PARAM_PERIOD_TIME,
|
||||
PCM_PARAM_PERIOD_SIZE,
|
||||
PCM_PARAM_PERIOD_BYTES,
|
||||
PCM_PARAM_PERIODS,
|
||||
PCM_PARAM_BUFFER_TIME,
|
||||
PCM_PARAM_BUFFER_SIZE,
|
||||
PCM_PARAM_BUFFER_BYTES,
|
||||
PCM_PARAM_TICK_TIME,
|
||||
};
|
||||
|
||||
/* Mixer control types */
|
||||
enum mixer_ctl_type {
|
||||
MIXER_CTL_TYPE_BOOL,
|
||||
MIXER_CTL_TYPE_INT,
|
||||
MIXER_CTL_TYPE_ENUM,
|
||||
MIXER_CTL_TYPE_BYTE,
|
||||
MIXER_CTL_TYPE_IEC958,
|
||||
MIXER_CTL_TYPE_INT64,
|
||||
MIXER_CTL_TYPE_UNKNOWN,
|
||||
|
||||
MIXER_CTL_TYPE_MAX,
|
||||
};
|
||||
|
||||
/* Open and close a stream */
|
||||
struct pcm *pcm_open(unsigned int card, unsigned int device,
|
||||
unsigned int flags, struct pcm_config *config);
|
||||
int pcm_close(struct pcm *pcm);
|
||||
int pcm_is_ready(struct pcm *pcm);
|
||||
|
||||
/* Obtain the parameters for a PCM */
|
||||
struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
|
||||
unsigned int flags);
|
||||
void pcm_params_free(struct pcm_params *pcm_params);
|
||||
|
||||
struct pcm_mask *pcm_params_get_mask(struct pcm_params *pcm_params,
|
||||
enum pcm_param param);
|
||||
unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
|
||||
enum pcm_param param);
|
||||
void pcm_params_set_min(struct pcm_params *pcm_params,
|
||||
enum pcm_param param, unsigned int val);
|
||||
unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
|
||||
enum pcm_param param);
|
||||
void pcm_params_set_max(struct pcm_params *pcm_params,
|
||||
enum pcm_param param, unsigned int val);
|
||||
|
||||
/* Converts the pcm parameters to a human readable string.
|
||||
* The string parameter is a caller allocated buffer of size bytes,
|
||||
* which is then filled up to size - 1 and null terminated,
|
||||
* if size is greater than zero.
|
||||
* The return value is the number of bytes copied to string
|
||||
* (not including null termination) if less than size; otherwise,
|
||||
* the number of bytes required for the buffer.
|
||||
*/
|
||||
int pcm_params_to_string(struct pcm_params *params, char *string, unsigned int size);
|
||||
|
||||
/* Returns 1 if the pcm_format is present (format bit set) in
|
||||
* the pcm_params structure; 0 otherwise, or upon unrecognized format.
|
||||
*/
|
||||
int pcm_params_format_test(struct pcm_params *params, enum pcm_format format);
|
||||
|
||||
/* Set and get config */
|
||||
int pcm_get_config(struct pcm *pcm, struct pcm_config *config);
|
||||
int pcm_set_config(struct pcm *pcm, struct pcm_config *config);
|
||||
|
||||
/* Returns a human readable reason for the last error */
|
||||
const char *pcm_get_error(struct pcm *pcm);
|
||||
|
||||
/* Returns the sample size in bits for a PCM format.
|
||||
* As with ALSA formats, this is the storage size for the format, whereas the
|
||||
* format represents the number of significant bits. For example,
|
||||
* PCM_FORMAT_S24_LE uses 32 bits of storage.
|
||||
*/
|
||||
unsigned int pcm_format_to_bits(enum pcm_format format);
|
||||
|
||||
/* Returns the buffer size (int frames) that should be used for pcm_write. */
|
||||
unsigned int pcm_get_buffer_size(struct pcm *pcm);
|
||||
unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames);
|
||||
unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes);
|
||||
|
||||
/* Returns the pcm latency in ms */
|
||||
unsigned int pcm_get_latency(struct pcm *pcm);
|
||||
|
||||
/* Returns available frames in pcm buffer and corresponding time stamp.
|
||||
* The clock is CLOCK_MONOTONIC if flag PCM_MONOTONIC was specified in pcm_open,
|
||||
* otherwise the clock is CLOCK_REALTIME.
|
||||
* For an input stream, frames available are frames ready for the
|
||||
* application to read.
|
||||
* For an output stream, frames available are the number of empty frames available
|
||||
* for the application to write.
|
||||
*/
|
||||
int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
|
||||
struct timespec *tstamp);
|
||||
|
||||
/* Returns the subdevice on which the pcm has been opened */
|
||||
unsigned int pcm_get_subdevice(struct pcm *pcm);
|
||||
|
||||
/* Write data to the fifo.
|
||||
* Will start playback on the first write or on a write that
|
||||
* occurs after a fifo underrun.
|
||||
*/
|
||||
int pcm_write(struct pcm *pcm, const void *data, unsigned int count);
|
||||
int pcm_read(struct pcm *pcm, void *data, unsigned int count);
|
||||
|
||||
/*
|
||||
* mmap() support.
|
||||
*/
|
||||
int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count);
|
||||
int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count);
|
||||
int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
|
||||
unsigned int *frames);
|
||||
int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames);
|
||||
int pcm_mmap_avail(struct pcm *pcm);
|
||||
|
||||
/* Returns current read/write position in the mmap buffer with associated time stamp.
|
||||
*/
|
||||
int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr, struct timespec *tstamp);
|
||||
|
||||
/* Prepare the PCM substream to be triggerable */
|
||||
int pcm_prepare(struct pcm *pcm);
|
||||
/* Start and stop a PCM channel that doesn't transfer data */
|
||||
int pcm_start(struct pcm *pcm);
|
||||
int pcm_stop(struct pcm *pcm);
|
||||
|
||||
/* ioctl function for PCM driver */
|
||||
int pcm_ioctl(struct pcm *pcm, int request, ...);
|
||||
|
||||
/* Interrupt driven API */
|
||||
int pcm_wait(struct pcm *pcm, int timeout);
|
||||
int pcm_get_poll_fd(struct pcm *pcm);
|
||||
|
||||
/* Change avail_min after the stream has been opened with no need to stop the stream.
|
||||
* Only accepted if opened with PCM_MMAP and PCM_NOIRQ flags
|
||||
*/
|
||||
int pcm_set_avail_min(struct pcm *pcm, int avail_min);
|
||||
|
||||
/*
|
||||
* MIXER API
|
||||
*/
|
||||
|
||||
struct mixer;
|
||||
struct mixer_ctl;
|
||||
|
||||
/* Open and close a mixer */
|
||||
struct mixer *mixer_open(unsigned int card);
|
||||
void mixer_close(struct mixer *mixer);
|
||||
|
||||
/* Get info about a mixer */
|
||||
const char *mixer_get_name(struct mixer *mixer);
|
||||
|
||||
/* Obtain mixer controls */
|
||||
unsigned int mixer_get_num_ctls(struct mixer *mixer);
|
||||
struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id);
|
||||
struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name);
|
||||
|
||||
/* Get info about mixer controls */
|
||||
const char *mixer_ctl_get_name(struct mixer_ctl *ctl);
|
||||
enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl);
|
||||
const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl);
|
||||
unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl);
|
||||
unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl);
|
||||
const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
|
||||
unsigned int enum_id);
|
||||
|
||||
/* Some sound cards update their controls due to external events,
|
||||
* such as HDMI EDID byte data changing when an HDMI cable is
|
||||
* connected. This API allows the count of elements to be updated.
|
||||
*/
|
||||
void mixer_ctl_update(struct mixer_ctl *ctl);
|
||||
|
||||
/* Set and get mixer controls */
|
||||
int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id);
|
||||
int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent);
|
||||
|
||||
int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id);
|
||||
int mixer_ctl_is_access_tlv_rw(struct mixer_ctl *ctl);
|
||||
int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count);
|
||||
int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value);
|
||||
int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count);
|
||||
int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string);
|
||||
|
||||
/* Determine range of integer mixer controls */
|
||||
int mixer_ctl_get_range_min(struct mixer_ctl *ctl);
|
||||
int mixer_ctl_get_range_max(struct mixer_ctl *ctl);
|
||||
|
||||
int mixer_subscribe_events(struct mixer *mixer, int subscribe);
|
||||
int mixer_wait_event(struct mixer *mixer, int timeout);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
220
app/src/main/java/kr/co/rito/osicmanager/FileDownloader.java
Normal file
@ -0,0 +1,220 @@
|
||||
package kr.co.rito.osicmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class FileDownloader extends Thread {
|
||||
private static String TAG = "FileDownloader";
|
||||
int DOWNLOAD_STATUS_PROCESS = 0;
|
||||
int DOWNLOAD_STATUS_SUCCESS = 1;
|
||||
int DOWNLOAD_STATUS_FAILED = 2;
|
||||
private int mDownloadStatus = DOWNLOAD_STATUS_PROCESS;
|
||||
private String mJsonUrlPath;
|
||||
private LinkedBlockingQueue<DownInfo> mDownList;
|
||||
private Context mContext;
|
||||
private boolean mIsAlive = false;
|
||||
|
||||
public class DownInfo {
|
||||
public String mUrl;
|
||||
public String mSaveDir;
|
||||
public String mSaveName;
|
||||
int mTryCount;
|
||||
}
|
||||
|
||||
private class CallbackToDownloadFile implements Callback {
|
||||
|
||||
private File directory;
|
||||
private File fileToBeDownloaded;
|
||||
|
||||
public CallbackToDownloadFile(String directory, String fileName) {
|
||||
this.directory = new File(directory);
|
||||
this.fileToBeDownloaded = new File(this.directory.getAbsolutePath() + "/" + fileName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
mDownloadStatus = DOWNLOAD_STATUS_FAILED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
if (!this.directory.exists()) {
|
||||
this.directory.mkdirs();
|
||||
}
|
||||
|
||||
if (this.fileToBeDownloaded.exists()) {
|
||||
this.fileToBeDownloaded.delete();
|
||||
}
|
||||
|
||||
try {
|
||||
this.fileToBeDownloaded.createNewFile();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
mDownloadStatus = DOWNLOAD_STATUS_FAILED;
|
||||
return;
|
||||
}
|
||||
|
||||
InputStream is = response.body().byteStream();
|
||||
OutputStream os = new FileOutputStream(this.fileToBeDownloaded);
|
||||
|
||||
final int BUFFER_SIZE = 2048;
|
||||
byte[] data = new byte[BUFFER_SIZE];
|
||||
|
||||
int count;
|
||||
long total = 0;
|
||||
|
||||
while ((count = is.read(data)) != -1) {
|
||||
total += count;
|
||||
os.write(data, 0, count);
|
||||
}
|
||||
|
||||
os.flush();
|
||||
os.close();
|
||||
is.close();
|
||||
|
||||
mDownloadStatus = DOWNLOAD_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
public FileDownloader(Context context) {
|
||||
mContext = context;
|
||||
mDownList = new LinkedBlockingQueue<DownInfo>(10000);
|
||||
}
|
||||
|
||||
public void addDownloadTarget(String url, String directory, String filename) {
|
||||
DownInfo info = new DownInfo();
|
||||
info.mUrl = url;
|
||||
info.mSaveDir = directory;
|
||||
info.mSaveName = filename;
|
||||
info.mTryCount = 0;
|
||||
mDownList.offer(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mIsAlive = true;
|
||||
while (mIsAlive) {
|
||||
if (mDownList.size() > 0) {
|
||||
boolean trouble = false;
|
||||
DownInfo info = mDownList.poll();
|
||||
|
||||
try {
|
||||
File destDirectory = new File(info.mSaveDir);
|
||||
if (!destDirectory.exists()) {
|
||||
destDirectory.mkdir();
|
||||
}
|
||||
} catch(Exception e) {
|
||||
trouble = true;
|
||||
}
|
||||
|
||||
if(trouble)
|
||||
continue;
|
||||
|
||||
boolean allowUntrusted = true;
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
OkHttpClient.Builder clientBuilder = client.newBuilder().readTimeout(10, TimeUnit.SECONDS);
|
||||
if (allowUntrusted) {
|
||||
final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
X509Certificate[] cArrr = new X509Certificate[0];
|
||||
return cArrr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(final X509Certificate[] chain,
|
||||
final String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(final X509Certificate[] chain,
|
||||
final String authType) throws CertificateException {
|
||||
}
|
||||
}};
|
||||
|
||||
|
||||
try {
|
||||
SSLContext sslContext = SSLContext.getInstance("SSL");
|
||||
|
||||
sslContext.init(null, trustAllCerts, new SecureRandom());
|
||||
clientBuilder.sslSocketFactory(sslContext.getSocketFactory());
|
||||
|
||||
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
|
||||
@Override
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
Log.d(TAG, "Trust Host :" + hostname);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
clientBuilder.hostnameVerifier(hostnameVerifier);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
OkHttpClient httpClient = clientBuilder.build();
|
||||
FileDownloader.CallbackToDownloadFile cb = new FileDownloader.CallbackToDownloadFile(info.mSaveDir, info.mSaveName);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(info.mUrl)
|
||||
.build();
|
||||
|
||||
httpClient.newCall(request).enqueue(cb);
|
||||
|
||||
while (mDownloadStatus == DOWNLOAD_STATUS_PROCESS) {
|
||||
try {
|
||||
sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (mDownloadStatus == DOWNLOAD_STATUS_FAILED) {
|
||||
info.mTryCount++;
|
||||
if (info.mTryCount < 10) {
|
||||
mDownList.offer(info);
|
||||
}
|
||||
}
|
||||
|
||||
MainActivity.INSTANCE.doSync();
|
||||
}
|
||||
|
||||
try {
|
||||
sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
386
app/src/main/java/kr/co/rito/osicmanager/JsonManager.java
Normal file
@ -0,0 +1,386 @@
|
||||
package kr.co.rito.osicmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
class JsonManager extends Thread {
|
||||
private static String TAG = "JsonManager";
|
||||
int DOWNLOAD_STATUS_PROCESS = 0;
|
||||
int DOWNLOAD_STATUS_SUCCESS = 1;
|
||||
int DOWNLOAD_STATUS_FAILED = 2;
|
||||
private int mDownloadStatus = DOWNLOAD_STATUS_PROCESS;
|
||||
private String mJsonUrlPath;
|
||||
private Context mContext;
|
||||
|
||||
private class CallbackToDownloadFile implements Callback {
|
||||
|
||||
private File directory;
|
||||
private File fileToBeDownloaded;
|
||||
|
||||
public CallbackToDownloadFile(String directory, String fileName) {
|
||||
this.directory = new File(directory);
|
||||
this.fileToBeDownloaded = new File(this.directory.getAbsolutePath() + "/" + fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
mDownloadStatus = DOWNLOAD_STATUS_FAILED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
if (!this.directory.exists()) {
|
||||
this.directory.mkdirs();
|
||||
}
|
||||
|
||||
if (this.fileToBeDownloaded.exists()) {
|
||||
this.fileToBeDownloaded.delete();
|
||||
}
|
||||
|
||||
try {
|
||||
this.fileToBeDownloaded.createNewFile();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
mDownloadStatus = DOWNLOAD_STATUS_FAILED;
|
||||
return;
|
||||
}
|
||||
|
||||
InputStream is = response.body().byteStream();
|
||||
OutputStream os = new FileOutputStream(this.fileToBeDownloaded);
|
||||
|
||||
final int BUFFER_SIZE = 2048;
|
||||
byte[] data = new byte[BUFFER_SIZE];
|
||||
|
||||
int count;
|
||||
long total = 0;
|
||||
|
||||
while ((count = is.read(data)) != -1) {
|
||||
total += count;
|
||||
os.write(data, 0, count);
|
||||
}
|
||||
|
||||
os.flush();
|
||||
os.close();
|
||||
is.close();
|
||||
|
||||
mDownloadStatus = DOWNLOAD_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
public JsonManager(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void setDownloadPath(String jsonUrl) {
|
||||
mJsonUrlPath = jsonUrl;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if(mJsonUrlPath == null)
|
||||
return;
|
||||
|
||||
boolean allowUntrusted = true;
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
OkHttpClient.Builder clientBuilder = client.newBuilder().readTimeout(10, TimeUnit.SECONDS);
|
||||
if(allowUntrusted) {
|
||||
final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
X509Certificate[] cArrr = new X509Certificate[0];
|
||||
return cArrr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(final X509Certificate[] chain,
|
||||
final String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(final X509Certificate[] chain,
|
||||
final String authType) throws CertificateException {
|
||||
}
|
||||
}};
|
||||
|
||||
|
||||
try {
|
||||
SSLContext sslContext = SSLContext.getInstance("SSL");
|
||||
|
||||
sslContext.init(null, trustAllCerts, new SecureRandom());
|
||||
clientBuilder.sslSocketFactory(sslContext.getSocketFactory());
|
||||
|
||||
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
|
||||
@Override
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
Log.d(TAG, "Trust Host :" + hostname);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
clientBuilder.hostnameVerifier( hostnameVerifier);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Request request;
|
||||
OkHttpClient httpClient;
|
||||
CallbackToDownloadFile cb;
|
||||
int retry_count = 0;
|
||||
do {
|
||||
mDownloadStatus = DOWNLOAD_STATUS_PROCESS;
|
||||
httpClient = clientBuilder.build();
|
||||
cb = new CallbackToDownloadFile("/tmp", "schedule.json");
|
||||
request = new Request.Builder()
|
||||
.url(mJsonUrlPath)
|
||||
.build();
|
||||
|
||||
httpClient.newCall(request).enqueue(cb);
|
||||
|
||||
while (mDownloadStatus == DOWNLOAD_STATUS_PROCESS) {
|
||||
try {
|
||||
sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (mDownloadStatus == DOWNLOAD_STATUS_FAILED) {
|
||||
retry_count++;
|
||||
try {
|
||||
sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} while(retry_count < 10 && mDownloadStatus == DOWNLOAD_STATUS_FAILED);
|
||||
|
||||
if (mDownloadStatus == DOWNLOAD_STATUS_FAILED) {
|
||||
Log.w(TAG, "Failed to download JSON [" + mJsonUrlPath + "]");
|
||||
return;
|
||||
} else if (mDownloadStatus == DOWNLOAD_STATUS_SUCCESS) {
|
||||
Log.i(TAG, "Success to download JSON [" + mJsonUrlPath + "] at retry count : " + retry_count);
|
||||
}
|
||||
|
||||
File sourceFile = new File("/tmp/schedule.json");
|
||||
String destPath = MainActivity.INSTANCE.mSDSavePath;
|
||||
try {
|
||||
File destDirectory = new File(destPath);
|
||||
if (!destDirectory.exists()) {
|
||||
destDirectory.mkdir();
|
||||
}
|
||||
} catch(Exception e) {
|
||||
MainActivity.INSTANCE.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(MainActivity.INSTANCE, "MicroSD 동작에 문제가 있습니다.", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
|
||||
mDownloadStatus = DOWNLOAD_STATUS_FAILED;
|
||||
return;
|
||||
}
|
||||
|
||||
SystemUtil.TraceLog("JsonManager Trace Before [File destFile = new File]");
|
||||
File destFile = new File(destPath + "/schedule.json");
|
||||
SystemUtil.copyFile(sourceFile, destFile);
|
||||
MainActivity.INSTANCE.doSync();
|
||||
|
||||
SystemUtil.TraceLog("JsonManager Trace Before [File checkFile = new File]");
|
||||
File checkFile = new File(destPath + "/schedule.json.check");
|
||||
if(checkFile.exists()) {
|
||||
checkFile.delete();
|
||||
MainActivity.INSTANCE.doSync();
|
||||
}
|
||||
|
||||
JsonResult jsonResult = loadJsonData();
|
||||
if(jsonResult == null) {
|
||||
Log.w(TAG, "Failed to download JSON!!!!. jsonResult is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if(jsonResult.mType.equalsIgnoreCase(MainActivity.MANAGER_JSON_ORDER_TYPE_PLAY_CONTENTS)) { // Download Contents Files
|
||||
for(int i = 0; i < jsonResult.mFileCount;i++) {
|
||||
mDownloadStatus = DOWNLOAD_STATUS_PROCESS;
|
||||
|
||||
String cksum = MainActivity.INSTANCE.getFileCksum(MainActivity.INSTANCE.mSDSavePath + "/" + jsonResult.mFilePathList[i]);
|
||||
if(cksum.equalsIgnoreCase(jsonResult.mFileCksumList[i])) {
|
||||
Log.d(TAG, "Already downloaded file : " + jsonResult.mFilePathList[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
for(int retry = 0;retry < 3;retry++) {
|
||||
cb = new CallbackToDownloadFile(MainActivity.INSTANCE.mSDSavePath, jsonResult.mFilePathList[i]);
|
||||
|
||||
request = new Request.Builder()
|
||||
.url(jsonResult.mFileUrlList[i])
|
||||
.build();
|
||||
|
||||
Log.d(TAG, "Start Download File : " + jsonResult.mFileUrlList[i]);
|
||||
|
||||
httpClient.newCall(request).enqueue(cb);
|
||||
|
||||
while (mDownloadStatus == DOWNLOAD_STATUS_PROCESS) {
|
||||
try {
|
||||
sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
MainActivity.INSTANCE.doSync();
|
||||
|
||||
cksum = MainActivity.INSTANCE.getFileCksum(MainActivity.INSTANCE.mSDSavePath + "/" + jsonResult.mFilePathList[i]);
|
||||
if (cksum.equalsIgnoreCase(jsonResult.mFileCksumList[i])) {
|
||||
Log.d(TAG, "다운로드 파일 정상 [" + jsonResult.mFilePathList[i] + "]");
|
||||
break;
|
||||
} else {
|
||||
Log.d(TAG, "다운로드 파일 체크섬 불일치 [" + jsonResult.mFilePathList[i] + "]. 재시도 횟수 : " + (retry + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "Contents Download Complete...");
|
||||
|
||||
checkFile = new File(destPath + "/schedule.json.check");
|
||||
if(!checkFile.exists()) {
|
||||
try {
|
||||
checkFile.createNewFile();
|
||||
MainActivity.INSTANCE.doSync();
|
||||
} catch(Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
File backupFile = new File(destPath + "/file-schedule.json");
|
||||
SystemUtil.copyFile(sourceFile, backupFile);
|
||||
MainActivity.INSTANCE.doSync();
|
||||
|
||||
Log.d(TAG, "Do updateJsonTargetResult as File-Schedule...");
|
||||
MainActivity.INSTANCE.mJsonScheduler.updateJsonTargetResult("file", jsonResult);
|
||||
}
|
||||
|
||||
MainActivity.INSTANCE.mJsonScheduler.updateJsonResult(jsonResult);
|
||||
MainActivity.INSTANCE.mJsonScheduler.prepareJsonSchedule();
|
||||
}
|
||||
|
||||
public JsonResult loadJsonData() {
|
||||
return loadJsonData("schedule.json");
|
||||
}
|
||||
|
||||
public static JsonResult loadJsonData(String jsonFileName) {
|
||||
String destPath = MainActivity.INSTANCE.mSDSavePath;
|
||||
File destFile = new File(destPath + "/" + jsonFileName);
|
||||
try {
|
||||
FileInputStream stream = new FileInputStream(destFile);
|
||||
String jString = null;
|
||||
try {
|
||||
FileChannel fc = stream.getChannel();
|
||||
MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
|
||||
jString = Charset.defaultCharset().decode(bb).toString();
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
|
||||
return parseJson(jString);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static JsonResult parseJson(String jsonData) {
|
||||
JsonResult res = new JsonResult();
|
||||
try {
|
||||
JSONObject topObject = new JSONObject(jsonData);
|
||||
String jsonType = topObject.getString("type");
|
||||
|
||||
if(jsonType.equalsIgnoreCase(MainActivity.MANAGER_JSON_ORDER_TYPE_PLAY_IPCAST)) {
|
||||
res.mType = MainActivity.MANAGER_JSON_ORDER_TYPE_PLAY_IPCAST;
|
||||
JSONArray jsonArray = topObject.getJSONArray("data");
|
||||
res.mIpCount = jsonArray.length();
|
||||
res.mIpAddressList = new String[res.mIpCount];
|
||||
res.mIpTitleList = new String[res.mIpCount];
|
||||
res.mIpNumberList = new String[res.mIpCount];
|
||||
res.mIpSelectedIndex = 0;
|
||||
for (int i = 0; i < res.mIpCount; i++) {
|
||||
JSONObject jo = jsonArray.getJSONObject(i);
|
||||
|
||||
res.mIpAddressList[i] = jo.getString("address");
|
||||
if(res.mIpAddressList[i].startsWith("live://")) {
|
||||
res.mIpAddressList[i] = res.mIpAddressList[i].replace("live://", "udp://");
|
||||
if(!res.mIpAddressList[i].contains("@")) {
|
||||
res.mIpAddressList[i] = res.mIpAddressList[i].replace("udp://", "udp://@");
|
||||
}
|
||||
}
|
||||
res.mIpTitleList[i] = jo.getString("title");
|
||||
res.mIpNumberList[i] = jo.getString("ch_no");
|
||||
if(jo.getBoolean("selected"))
|
||||
res.mIpSelectedIndex = i;
|
||||
}
|
||||
} else if(jsonType.equalsIgnoreCase(MainActivity.MANAGER_JSON_ORDER_TYPE_PLAY_CARCAST)) {
|
||||
res.mType = MainActivity.MANAGER_JSON_ORDER_TYPE_PLAY_CARCAST;
|
||||
JSONArray carList = topObject.getJSONArray("data");
|
||||
JSONObject carData = carList.getJSONObject(0);
|
||||
res.mCarAddress = carData.getString("url");
|
||||
res.mCarCar = carData.getString("car");
|
||||
res.mCarStation = carData.getString("station");
|
||||
} else if(jsonType.equalsIgnoreCase(MainActivity.MANAGER_JSON_ORDER_TYPE_PLAY_CONTENTS)) {
|
||||
res.mType = MainActivity.MANAGER_JSON_ORDER_TYPE_PLAY_CONTENTS;
|
||||
JSONArray jsonArray = topObject.getJSONArray("data");
|
||||
res.mFileCount = jsonArray.length();
|
||||
res.mFileUrlList = new String[res.mFileCount];
|
||||
res.mFilePathList = new String[res.mFileCount];
|
||||
res.mFileTitleList = new String[res.mFileCount];
|
||||
res.mFileDurationList = new int[res.mFileCount];
|
||||
res.mFileCksumList = new String[res.mFileCount];
|
||||
for (int i = 0; i < res.mFileCount; i++) {
|
||||
JSONObject jo = jsonArray.getJSONObject(i);
|
||||
|
||||
res.mFileUrlList[i] = jo.getString("url");
|
||||
res.mFilePathList[i] = res.mFileUrlList[i].substring(res.mFileUrlList[i].lastIndexOf('/') + 1);
|
||||
res.mFileTitleList[i] = jo.getString("name");
|
||||
res.mFileDurationList[i] = jo.getInt("duration");
|
||||
res.mFileCksumList[i] = jo.getString("cksum");
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
25
app/src/main/java/kr/co/rito/osicmanager/JsonResult.java
Normal file
@ -0,0 +1,25 @@
|
||||
package kr.co.rito.osicmanager;
|
||||
|
||||
public class JsonResult {
|
||||
public String mType;
|
||||
|
||||
// For IpCast
|
||||
public int mIpCount;
|
||||
public int mIpSelectedIndex;
|
||||
public String[] mIpAddressList;
|
||||
public String[] mIpTitleList;
|
||||
public String[] mIpNumberList;
|
||||
|
||||
// For CarCast
|
||||
public String mCarAddress;
|
||||
public String mCarStation;
|
||||
public String mCarCar;
|
||||
|
||||
// For FileCast
|
||||
public int mFileCount;
|
||||
public String[] mFileUrlList;
|
||||
public String[] mFileTitleList;
|
||||
public int[] mFileDurationList;
|
||||
public String[] mFilePathList;
|
||||
public String[] mFileCksumList;
|
||||
}
|
4714
app/src/main/java/kr/co/rito/osicmanager/MainActivity.java
Normal file
55
app/src/main/java/kr/co/rito/osicmanager/RWebView.java
Normal file
@ -0,0 +1,55 @@
|
||||
package kr.co.rito.osicmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.AvoidXfermode;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class RWebView extends WebView {
|
||||
public RWebView(Context context) {
|
||||
super(context);
|
||||
setLayerType(LAYER_TYPE_SOFTWARE, null);
|
||||
|
||||
}
|
||||
|
||||
public RWebView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
Paint p = new Paint();
|
||||
p.setARGB(255, 255, 0, 255);
|
||||
int removeColor = p.getColor();
|
||||
p.setXfermode(new AvoidXfermode(removeColor, 0, AvoidXfermode.Mode.TARGET));
|
||||
setLayerType(LAYER_TYPE_SOFTWARE, p);
|
||||
}
|
||||
|
||||
public RWebView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
Paint p = new Paint();
|
||||
p.setARGB(255, 255, 0, 255);
|
||||
int removeColor = p.getColor();
|
||||
p.setXfermode(new AvoidXfermode(removeColor, 0, AvoidXfermode.Mode.TARGET));
|
||||
setLayerType(LAYER_TYPE_SOFTWARE, p);
|
||||
|
||||
}
|
||||
|
||||
public RWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
Paint p = new Paint();
|
||||
p.setARGB(255, 255, 0, 255);
|
||||
int removeColor = p.getColor();
|
||||
p.setXfermode(new AvoidXfermode(removeColor, 0, AvoidXfermode.Mode.TARGET));
|
||||
|
||||
setLayerType(LAYER_TYPE_SOFTWARE, p);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(android.graphics.Canvas canvas) {
|
||||
Paint p = new Paint();
|
||||
p.setARGB(255, 255, 0, 255);
|
||||
int removeColor = p.getColor();
|
||||
p.setAlpha(1); // if Alpha is 0 it doesn't work. I don't know why
|
||||
p.setXfermode(new AvoidXfermode(removeColor, 0, AvoidXfermode.Mode.TARGET));
|
||||
canvas.drawPaint(p);
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
}
|
167
app/src/main/java/kr/co/rito/osicmanager/SettingActivity.java
Normal file
@ -0,0 +1,167 @@
|
||||
package kr.co.rito.osicmanager;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import kr.co.rito.osicmanager.ui.main.PlaceholderFragment;
|
||||
import kr.co.rito.osicmanager.ui.main.SectionsPagerAdapter;
|
||||
|
||||
public class SettingActivity extends AppCompatActivity implements TabLayout.OnTabSelectedListener {
|
||||
|
||||
static int test = 0;
|
||||
Instrumentation mInstrument;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(R.layout.activity_setting);
|
||||
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
|
||||
|
||||
// SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
|
||||
// ViewPager viewPager = findViewById(R.id.view_pager);
|
||||
// viewPager.setAdapter(sectionsPagerAdapter);
|
||||
// TabLayout tabs = findViewById(R.id.tabs);
|
||||
// tabs.setupWithViewPager(viewPager);
|
||||
// tabs.addOnTabSelectedListener(this);
|
||||
// FloatingActionButton fab = findViewById(R.id.fab);
|
||||
//
|
||||
// mInstrument = new Instrumentation();
|
||||
//
|
||||
// fab.setOnClickListener(new View.OnClickListener() {
|
||||
// @Override
|
||||
// public void onClick(View view) {
|
||||
// Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||
// .setAction("Action", null).show();
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
|
||||
|
||||
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
|
||||
ViewPager viewPager = findViewById(R.id.view_pager);
|
||||
viewPager.setAdapter(sectionsPagerAdapter);
|
||||
TabLayout tabs = findViewById(R.id.tabs);
|
||||
tabs.setupWithViewPager(viewPager);
|
||||
tabs.addOnTabSelectedListener(this);
|
||||
FloatingActionButton fab = findViewById(R.id.fab);
|
||||
|
||||
mInstrument = new Instrumentation();
|
||||
|
||||
fab.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||
.setAction("Action", null).show();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
String tabName = tab.getText().toString();
|
||||
Log.w("RITO", "TabSelected : " + tabName);
|
||||
|
||||
tab.view.requestFocus();
|
||||
|
||||
/*ViewPager viewPager = findViewById(R.id.view_pager);
|
||||
viewPager.setCurrentItem(tab.getPosition());*/
|
||||
|
||||
/*TextView view = findViewById(R.id.textViewVersionValue);
|
||||
view.setText("" + test);
|
||||
test++;*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {
|
||||
String tabName = tab.getText().toString();
|
||||
if(tabName.equals("네트워크 설정")) {
|
||||
|
||||
} else if(tabName.equals("서버 설정")) {
|
||||
} else if(tabName.equals("기기 정보")) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
|
||||
}
|
||||
|
||||
private void makeKeyEvent(final int keycode) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
mInstrument.sendKeyDownUpSync(keycode);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if(event.getKeyCode() == 183) { // SETUP KEY
|
||||
// ViewPager viewPager = findViewById(R.id.view_pager);
|
||||
// SectionsPagerAdapter sectionsPagerAdapter = (SectionsPagerAdapter)viewPager.getAdapter();
|
||||
// PlaceholderFragment fragment = (PlaceholderFragment)sectionsPagerAdapter.getItem(1);
|
||||
// fragment.saveSettings();
|
||||
// sectionsPagerAdapter.getItem(1).onStop();
|
||||
// Intent intent = new Intent(this, MainActivity.class);
|
||||
// intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
// startActivity(intent);
|
||||
//SystemUtil.shellCommand("ritosysc SHELL-ORDER=killall kr.co.rito.osicmanager");
|
||||
SystemUtil.shellCommand("ritosysc SHELL-ORDER=am force-stop kr.co.rito.osicmanager");
|
||||
/*finish();*/
|
||||
return true;
|
||||
} else if(event.getKeyCode() == 200) { // VFD LEFT
|
||||
makeKeyEvent(KeyEvent.KEYCODE_DPAD_LEFT);
|
||||
return true;
|
||||
} else if(event.getKeyCode() == 201) { // VFD RIGHT
|
||||
makeKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT);
|
||||
return true;
|
||||
} else if(event.getKeyCode() == 202) { // VFD UP
|
||||
makeKeyEvent(KeyEvent.KEYCODE_DPAD_UP);
|
||||
return true;
|
||||
} else if(event.getKeyCode() == 203) { // VFD DOWN
|
||||
makeKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN);
|
||||
return true;
|
||||
} else if(event.getKeyCode() == 199) { // VFD OKAY
|
||||
makeKeyEvent(KeyEvent.KEYCODE_ENTER);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
369
app/src/main/java/kr/co/rito/osicmanager/SystemUtil.java
Normal file
@ -0,0 +1,369 @@
|
||||
package kr.co.rito.osicmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class SystemUtil {
|
||||
public final static String TAG = "OsicUtil";
|
||||
|
||||
public static String APP_MODE = "passive";
|
||||
|
||||
public static void shellCommand(String order) {
|
||||
try {
|
||||
Process process = Runtime.getRuntime().exec(order);
|
||||
process.waitFor();
|
||||
process.destroy();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void logStart() {
|
||||
String filePath = Environment.getExternalStorageDirectory() + "/logcat.txt";
|
||||
try {
|
||||
File logFile = new File(filePath);
|
||||
if (logFile.exists()) {
|
||||
Log.d(TAG, "logStart starTime=" + System.currentTimeMillis() + " fileLength=" + logFile.length());
|
||||
}
|
||||
if (logFile.exists() && logFile.length() > 100 * 1024 * 1024) {
|
||||
logFile.delete();
|
||||
}
|
||||
Runtime.getRuntime().exec(new String[]{"logcat", "-c"});
|
||||
Runtime.getRuntime().exec(new String[]{"logcat", "-f", filePath});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void shellCommand(String[] order) {
|
||||
try {
|
||||
Process process = Runtime.getRuntime().exec(order);
|
||||
process.waitFor();
|
||||
process.destroy();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void property_set(String key, String val) throws IllegalArgumentException {
|
||||
try {
|
||||
Class<?> SystemProperties = Class.forName("android.os.SystemProperties");
|
||||
//Parameters Types
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class[] paramTypes = { String.class, String.class };
|
||||
Method set = SystemProperties.getMethod("set", paramTypes);
|
||||
//Parameters
|
||||
Object[] params = { key, val };
|
||||
set.invoke(SystemProperties, params); }
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static String property_get(String key) {
|
||||
String ret = "";
|
||||
try {
|
||||
Class<?> SystemProperties = Class.forName("android.os.SystemProperties");
|
||||
//Parameters Types
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class[] paramTypes = { String.class };
|
||||
Method get = SystemProperties.getMethod("get", paramTypes);
|
||||
//Parameters
|
||||
Object[] params = { key };
|
||||
ret = (String) get.invoke(SystemProperties, params);
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
ret = "";
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "IllegalArgumentException e: "+ e.toString());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ret = "";
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "Exception e: "+ e.toString());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static String getVersionInfo(Context context) {
|
||||
String version = "Unknown";
|
||||
PackageInfo packageInfo;
|
||||
|
||||
if (context == null) {
|
||||
return version;
|
||||
}
|
||||
try {
|
||||
packageInfo = context.getApplicationContext()
|
||||
.getPackageManager()
|
||||
.getPackageInfo(context.getApplicationContext().getPackageName(), 0 );
|
||||
version = packageInfo.versionName;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "getVersionInfo :" + e.getMessage());
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
public static void copyFile(File sourceFile, File destFile) {
|
||||
if (!destFile.getParentFile().exists())
|
||||
destFile.getParentFile().mkdirs();
|
||||
|
||||
if (!destFile.exists()) {
|
||||
try {
|
||||
destFile.createNewFile();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FileChannel source = null;
|
||||
FileChannel destination = null;
|
||||
|
||||
try {
|
||||
source = new FileInputStream(sourceFile).getChannel();
|
||||
destination = new FileOutputStream(destFile).getChannel();
|
||||
destination.transferFrom(source, 0, source.size());
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (source != null) {
|
||||
try {
|
||||
source.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
if (destination != null) {
|
||||
try {
|
||||
destination.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final String TRACE_LOG_LEVEL_1 = "1";
|
||||
public static final String TRACE_LOG_LEVEL_2 = "2";
|
||||
public static final String TRACE_LOG_LEVEL_3 = "3";
|
||||
public static final String TRACE_LOG_LEVEL_4 = "4";
|
||||
public static final String TRACE_LOG_LEVEL_5 = "5";
|
||||
|
||||
|
||||
public static void TraceLineLog(String message) {
|
||||
StackTraceElement element = Thread.currentThread().getStackTrace()[3]; // 호출한 메서드의 스택 정보
|
||||
String fullMessage = String.format("%s (%s:%d)", message, element.getFileName(), element.getLineNumber());
|
||||
Log.d(TAG, fullMessage);
|
||||
}
|
||||
|
||||
|
||||
public static void TraceLog(String log) {
|
||||
TraceLog(TRACE_LOG_LEVEL_1, log);
|
||||
}
|
||||
|
||||
public static void TraceLog(String level, String log) {
|
||||
String traceMode = property_get("sys.rito.trace");
|
||||
if(traceMode.equals(level)) {
|
||||
Log.d("OsicTrace", log);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StorageInfo {
|
||||
|
||||
public final String path;
|
||||
public final boolean readonly;
|
||||
public final boolean removable;
|
||||
public final boolean usb;
|
||||
public final boolean mmc;
|
||||
public final int number;
|
||||
|
||||
StorageInfo(String path, boolean readonly, boolean removable, int number, boolean usb, boolean mmc) {
|
||||
this.path = path;
|
||||
this.readonly = readonly;
|
||||
this.removable = removable;
|
||||
this.number = number;
|
||||
this.usb = usb;
|
||||
this.mmc = mmc;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
StringBuilder res = new StringBuilder();
|
||||
if (!removable) {
|
||||
res.append("Internal SD card");
|
||||
} else if (number > 1) {
|
||||
res.append("SD card " + number);
|
||||
} else {
|
||||
if(usb) {
|
||||
res.append("USB disk");
|
||||
} else {
|
||||
res.append("SD card");
|
||||
}
|
||||
}
|
||||
if (readonly) {
|
||||
res.append(" (Read only)");
|
||||
}
|
||||
return res.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSDCardPath() {
|
||||
List<StorageInfo> list = getStorageList();
|
||||
for(StorageInfo info : list) {
|
||||
if(info.mmc)
|
||||
return info.path;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void adjustDataVolume(byte[] data, int length, int volume) {
|
||||
if(volume < 100) {
|
||||
for(int j = 0;j < length && j < data.length - 1;j += 2) {
|
||||
short vols = (short)((int)((data[j + 1] << 8) & 0x00FF00) + (int)(data[j] & 0x00FF));
|
||||
double volf = vols;
|
||||
volf *= (double)volume;
|
||||
volf /= (double)100;
|
||||
vols = (short)volf;
|
||||
data[j + 1] = (byte)(((vols & 0x00FF00) >> 8) & 0x00FF);
|
||||
data[j] = (byte)((vols & 0x00FF));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<StorageInfo> getStorageList() {
|
||||
|
||||
List<StorageInfo> list = new ArrayList<StorageInfo>();
|
||||
String def_path = Environment.getExternalStorageDirectory().getPath();
|
||||
boolean def_path_removable = Environment.isExternalStorageRemovable();
|
||||
String def_path_state = Environment.getExternalStorageState();
|
||||
boolean def_path_available = def_path_state.equals(Environment.MEDIA_MOUNTED)
|
||||
|| def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
|
||||
boolean def_path_readonly = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
|
||||
|
||||
HashSet<String> paths = new HashSet<String>();
|
||||
int cur_removable_number = 1;
|
||||
|
||||
if (def_path_available) {
|
||||
paths.add(def_path);
|
||||
list.add(0, new StorageInfo(def_path, def_path_readonly, def_path_removable, def_path_removable ? cur_removable_number++ : -1, false, false));
|
||||
}
|
||||
|
||||
BufferedReader buf_reader = null;
|
||||
try {
|
||||
buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
|
||||
String line;
|
||||
Log.d(TAG, "/proc/mounts");
|
||||
while ((line = buf_reader.readLine()) != null) {
|
||||
Log.d(TAG, line);
|
||||
if (line.contains("vfat") || line.contains("/mnt")) {
|
||||
StringTokenizer tokens = new StringTokenizer(line, " ");
|
||||
String mount_device = tokens.nextToken(); //device
|
||||
String mount_point = tokens.nextToken(); //mount point
|
||||
if (paths.contains(mount_point)) {
|
||||
continue;
|
||||
}
|
||||
String unused = tokens.nextToken(); //file system
|
||||
List<String> flags = Arrays.asList(tokens.nextToken().split(",")); //flags
|
||||
boolean readonly = flags.contains("ro");
|
||||
|
||||
if (line.contains("/dev/block/vold")) {
|
||||
if (!line.contains("/mnt/secure")
|
||||
&& !line.contains("/mnt/asec")
|
||||
&& !line.contains("/mnt/obb")
|
||||
&& !line.contains("/dev/mapper")
|
||||
&& !line.contains("tmpfs")) {
|
||||
boolean usb = false;
|
||||
boolean mmc = false;
|
||||
paths.add(mount_point);
|
||||
if(mount_device.contains("/dev/block/vold/public:8"))
|
||||
usb = true;
|
||||
if(mount_device.contains("/dev/block/vold/public:179"))
|
||||
mmc = true;
|
||||
|
||||
list.add(new StorageInfo(mount_point, readonly, true, cur_removable_number++, usb, mmc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (FileNotFoundException ex) {
|
||||
ex.printStackTrace();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
if (buf_reader != null) {
|
||||
try {
|
||||
buf_reader.close();
|
||||
} catch (IOException ex) {}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static boolean saveByteArrayToFile(String filePath, byte[] data, int length) {
|
||||
if (filePath == null || data == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
File file = new File(filePath);
|
||||
fos = new FileOutputStream(file, true);
|
||||
fos.write(data, 0, length);
|
||||
fos.flush();
|
||||
//return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public native static String getIPAddress();
|
||||
public native static String getConfigValue(String target);
|
||||
public native static void setConfigValue(String target, String value);
|
||||
|
||||
public static int GPIO_IN = 0;
|
||||
public static int GPIO_OUT = 1;
|
||||
public static int GPIO_LOW = 0;
|
||||
public static int GPIO_HIGH = 1;
|
||||
public native static int getPinValue(int pinNo);
|
||||
public native static int setPinValue(int pinNo, int value);
|
||||
public native static int pinSetup(int pinNo, int pinDirection);
|
||||
public native static int playWaveFile(String filePath, int device);
|
||||
public native static int setupAudioOut(int device);
|
||||
public native static int writeAudioOut(byte[] buffer, int size);
|
||||
}
|
192
app/src/main/java/kr/co/rito/osicmanager/TickerManager.java
Normal file
@ -0,0 +1,192 @@
|
||||
package kr.co.rito.osicmanager;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class TickerManager {
|
||||
private final static String TAG = "TickerManager";
|
||||
Activity mActivity;
|
||||
TextView mTickerView;
|
||||
String mTickerText;
|
||||
Rect mPosition;
|
||||
int mForeAlpha;
|
||||
int mBackAlpha;
|
||||
int mForeColor;
|
||||
int mBackColor;
|
||||
int mTickCount;
|
||||
float mTickSpeed;
|
||||
boolean mScriptActive;
|
||||
Script mLastScript;
|
||||
|
||||
class Script extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.i(TAG, "Script Start");
|
||||
mScriptActive = true;
|
||||
// mTickerView.setSelected(true);
|
||||
// try {
|
||||
// sleep(50);
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// mTickerView.setSelected(false);
|
||||
// try {
|
||||
// sleep(50);
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
try {
|
||||
sleep(50);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
MainActivity.INSTANCE.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mTickerView.setSelected(true);
|
||||
}
|
||||
});
|
||||
try {
|
||||
sleep(20);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//mTickerView.setSelected(true);
|
||||
while(!mTickerView.isMarqueeActive()) {
|
||||
try {
|
||||
sleep(10);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
mTickerView.setAlpha(1);
|
||||
|
||||
mTickerView.setMarqueeSpeed(mTickSpeed);
|
||||
while(!mTickerView.isMarqueeStopped()) {
|
||||
try {
|
||||
sleep(10);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
// mTickerView.setAlpha(0.01f);
|
||||
// mTickerView.setSelected(false);
|
||||
|
||||
MainActivity.INSTANCE.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mTickerView.setAlpha(0.01f);
|
||||
mTickerView.setSelected(false);
|
||||
}
|
||||
});
|
||||
|
||||
Log.i(TAG, "Script End");
|
||||
}
|
||||
}
|
||||
|
||||
public TickerManager(Activity activity, TextView tickerView) {
|
||||
mActivity = activity;
|
||||
mTickerView = tickerView;
|
||||
mForeAlpha = 0xFF000000;
|
||||
mBackAlpha = 0xFE000000;
|
||||
}
|
||||
|
||||
public void setupTicker(String text, Rect position, int foreColor, int backColor, int tickCount, float tickSpeed) {
|
||||
mTickerText = text;
|
||||
mPosition = position;
|
||||
mForeColor = (mForeAlpha | foreColor);
|
||||
mBackColor = (mBackAlpha | backColor);
|
||||
mTickCount = tickCount;
|
||||
mTickSpeed = tickSpeed * 50;
|
||||
}
|
||||
|
||||
public void renderTicker() {
|
||||
Paint paint = new Paint();
|
||||
final float textSize = mPosition.height() * 0.5f;
|
||||
paint.setTextSize(textSize);
|
||||
float spaceWidth = paint.measureText(" ");
|
||||
float width = mPosition.width();
|
||||
int dupCount = (int)(width / spaceWidth) + 1;
|
||||
Log.e(TAG, String.format("[Position] Width : %d, Height : %d, spaceWidth : %2.2f, width : %2.2f, dupCount : %d", mPosition.width(), mPosition.height(), spaceWidth, width, dupCount));
|
||||
String curText = "";
|
||||
for(int i = 0;i < dupCount;i++)
|
||||
curText += " ";
|
||||
curText += mTickerText;
|
||||
final String _sendText = new String(curText);
|
||||
mScriptActive = false;
|
||||
|
||||
mActivity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
// if(mLastScript != null) {
|
||||
// try {
|
||||
// mLastScript.join();
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// try {
|
||||
// Thread.sleep(150);
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
|
||||
// mTickerView.setText("");
|
||||
// mTickerView.setSelected(true);
|
||||
// mTickerView.setSelected(false);
|
||||
|
||||
//mTickerView.setText(mTickerText);
|
||||
// FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
|
||||
// params.setMargins(mPosition.left, mPosition.top, mPosition.right, mPosition.bottom);
|
||||
// mTickerView.setLayoutParams(params);
|
||||
//mTickerView.layout(mPosition.left, mPosition.top, mPosition.right, mPosition.bottom);
|
||||
mTickerView.setX(mPosition.left);
|
||||
mTickerView.setY(mPosition.top);
|
||||
mTickerView.getLayoutParams().width = (mPosition.right - mPosition.left);
|
||||
mTickerView.getLayoutParams().height = (int)((mPosition.bottom - mPosition.top) * 1.0);
|
||||
mTickerView.setGravity(Gravity.CENTER);
|
||||
// mTickerView.setHeight((mPosition.bottom - mPosition.top));
|
||||
// mTickerView.setWidth((mPosition.right - mPosition.left));
|
||||
mTickerView.setTextSize(textSize);
|
||||
mTickerView.setText(_sendText);
|
||||
mTickerView.setHorizontallyScrolling(true);
|
||||
mTickerView.setSingleLine(true);
|
||||
mTickerView.setTextColor(mForeColor);
|
||||
mTickerView.setBackgroundColor(mBackColor);
|
||||
mTickerView.setMarqueeRepeatLimit(mTickCount);
|
||||
|
||||
|
||||
|
||||
//int curGravity = mTickerView.getGravity();
|
||||
// mTickerView.setGravity(Gravity.AXIS_PULL_BEFORE);
|
||||
// float spacing = mTickerView.getLetterSpacing();
|
||||
// mTickerView.setLetterSpacing(spacing + 0.1f);
|
||||
// mTickerView.setLetterSpacing(spacing);
|
||||
// mTickerView.setGravity(Gravity.CENTER_VERTICAL);
|
||||
// mTickerView.setAlpha(1);
|
||||
// mTickerView.invalidate();
|
||||
// mTickerView.bringToFront();
|
||||
|
||||
|
||||
mLastScript = new Script();
|
||||
mLastScript.start();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
128
app/src/main/java/kr/co/rito/osicmanager/TickerView.java
Normal file
@ -0,0 +1,128 @@
|
||||
package kr.co.rito.osicmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.TextureView;
|
||||
|
||||
public class TickerView extends TextureView {
|
||||
|
||||
RenderScript mRenderer;
|
||||
Rect mPosition;
|
||||
String mTickerText;
|
||||
int mForeColor;
|
||||
int mBackColor;
|
||||
|
||||
public TickerView(Context context) {
|
||||
super(context);
|
||||
setOpaque(false);
|
||||
}
|
||||
|
||||
public TickerView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setOpaque(false);
|
||||
}
|
||||
|
||||
public TickerView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
setOpaque(false);
|
||||
}
|
||||
|
||||
public TickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
setOpaque(false);
|
||||
}
|
||||
|
||||
private static Bitmap getTextImageWithSizeDetail(String _txt, String _fontName, float _text_size, int _height,
|
||||
int _colorCode, int _shadowColorCode, float _shadowOffsetX, float _shadowOffsetY, int align) {
|
||||
Paint textPaint = new Paint();
|
||||
textPaint.setTextSize(_text_size);
|
||||
float _measureSize = textPaint.measureText(_txt);
|
||||
int _width = (int)_measureSize + 1;
|
||||
|
||||
Bitmap textBitmap = Bitmap.createBitmap(_width, _height, Bitmap.Config.ARGB_8888);
|
||||
// textBitmap.eraseColor(0x8844ff44);
|
||||
Canvas canvas = new Canvas(textBitmap);
|
||||
Typeface typeface = Typeface.create(_fontName, Typeface.NORMAL);
|
||||
|
||||
setAutoTextSize(_text_size, textPaint, _txt, _width);
|
||||
textPaint.setAntiAlias(true);
|
||||
textPaint.setColor(_colorCode);
|
||||
textPaint.setShadowLayer(2f, _shadowOffsetX, _shadowOffsetY, _shadowColorCode);
|
||||
textPaint.setTypeface(typeface);
|
||||
Rect bounds = new Rect();
|
||||
textPaint.getTextBounds(_txt, 0, _txt.length(), bounds);
|
||||
float measureTxt = textPaint.measureText(_txt);
|
||||
float w = 0f, h = Math.abs(bounds.top); // 상단정렬
|
||||
// h = _height - Math.abs(bounds.bottom); //하단정렬
|
||||
if (align == -1) {
|
||||
h += (_height - h - Math.abs(bounds.bottom)) / 2;
|
||||
} else if (align == 0) {
|
||||
w = (_width - measureTxt) / 2;
|
||||
h += (_height - h - Math.abs(bounds.bottom)) / 2;
|
||||
} else if (align == 1) {
|
||||
w = _width - measureTxt;
|
||||
h += (_height - h - Math.abs(bounds.bottom)) / 2;
|
||||
}
|
||||
canvas.drawText(_txt, w, h, textPaint);
|
||||
return textBitmap;
|
||||
}
|
||||
|
||||
static void setAutoTextSize(float textSize, Paint paint, String text, int width) {
|
||||
paint.setTextSize(textSize);
|
||||
float _measureSize = paint.measureText(text);
|
||||
if (_measureSize >= width) {
|
||||
setAutoTextSize(--textSize, paint, text, width);
|
||||
}
|
||||
}
|
||||
|
||||
class RenderScript extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
int pos_x = mPosition.right;
|
||||
//Bitmap textBitmap = getTextImageWithSizeDetail(mTickerText, "", );
|
||||
|
||||
while(true) {
|
||||
Canvas canvas = lockCanvas();
|
||||
if (canvas != null) {
|
||||
Paint paint = new Paint();
|
||||
paint.setTextSize(mPosition.height());
|
||||
Paint clearPaint = new Paint();
|
||||
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||
canvas.drawPaint(clearPaint);
|
||||
canvas.drawRect(mPosition, paint);
|
||||
canvas.drawColor(mForeColor);
|
||||
canvas.drawText(mTickerText, pos_x, mPosition.top + 40, paint);
|
||||
unlockCanvasAndPost(canvas);
|
||||
}
|
||||
|
||||
try {
|
||||
sleep(33);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
pos_x -= 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setupTicker(String text, Rect position, int foreColor, int backColor) {
|
||||
mTickerText = text;
|
||||
mPosition = position;
|
||||
mForeColor = foreColor;
|
||||
mBackColor = backColor;
|
||||
}
|
||||
|
||||
public void renderTicker() {
|
||||
mRenderer = new RenderScript();
|
||||
//mRenderer.start();
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package kr.co.rito.osicmanager.ui.main;
|
||||
|
||||
import androidx.arch.core.util.Function;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Transformations;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
public class PageViewModel extends ViewModel {
|
||||
|
||||
private MutableLiveData<Integer> mIndex = new MutableLiveData<>();
|
||||
private LiveData<String> mText = Transformations.map(mIndex, new Function<Integer, String>() {
|
||||
@Override
|
||||
public String apply(Integer input) {
|
||||
return "Hello world from section: " + input;
|
||||
}
|
||||
});
|
||||
|
||||
public void setIndex(int index) {
|
||||
mIndex.setValue(index);
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return mIndex.getValue();
|
||||
}
|
||||
|
||||
public LiveData<String> getText() {
|
||||
return mText;
|
||||
}
|
||||
}
|
@ -0,0 +1,318 @@
|
||||
package kr.co.rito.osicmanager.ui.main;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import kr.co.rito.osicmanager.MainActivity;
|
||||
import kr.co.rito.osicmanager.R;
|
||||
import kr.co.rito.osicmanager.SystemUtil;
|
||||
|
||||
/**
|
||||
* A placeholder fragment containing a simple view.
|
||||
*/
|
||||
public class PlaceholderFragment extends Fragment {
|
||||
|
||||
private static final String ARG_SECTION_NUMBER = "section_number";
|
||||
|
||||
private PageViewModel pageViewModel;
|
||||
private boolean mIpReloadAlive = false;
|
||||
|
||||
public static PlaceholderFragment newInstance(int index) {
|
||||
PlaceholderFragment fragment = new PlaceholderFragment();
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(ARG_SECTION_NUMBER, index);
|
||||
fragment.setArguments(bundle);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
getActivity().getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
|
||||
|
||||
pageViewModel = ViewModelProviders.of(this).get(PageViewModel.class);
|
||||
int index = 1;
|
||||
if (getArguments() != null) {
|
||||
index = getArguments().getInt(ARG_SECTION_NUMBER);
|
||||
}
|
||||
pageViewModel.setIndex(index);
|
||||
}
|
||||
|
||||
private void infoUpdate(final View rootView) {
|
||||
Log.w("RITO", "infoUpdate()");
|
||||
TextView view = rootView.findViewById(R.id.textViewVersionValue);
|
||||
//String value = SystemUtil.property_get("rito.system.version");
|
||||
String value = SystemUtil.getVersionInfo(getContext());
|
||||
if(value.length() == 0) {
|
||||
value = "1";
|
||||
}
|
||||
view.setText(value);
|
||||
|
||||
view = rootView.findViewById(R.id.textViewInfoSerialValue);
|
||||
value = SystemUtil.property_get("rito.board.serial");
|
||||
view.setText(value);
|
||||
|
||||
view = rootView.findViewById(R.id.textViewInfoMacValue);
|
||||
value = SystemUtil.property_get("rito.board.mac");
|
||||
view.setText(value);
|
||||
|
||||
//SystemUtil _sysUtil = new SystemUtil();
|
||||
if(!mIpReloadAlive) {
|
||||
mIpReloadAlive = true;
|
||||
new Thread("ipreload") {
|
||||
@Override
|
||||
public void run() {
|
||||
while(mIpReloadAlive) {
|
||||
final TextView _view = rootView.findViewById(R.id.textViewInfoIpValue);
|
||||
final String _value = SystemUtil.getIPAddress();
|
||||
MainActivity.INSTANCE.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.w("RITO", "Draw IP : " + _value);
|
||||
_view.setText(_value);
|
||||
}});
|
||||
|
||||
try {
|
||||
sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
// view = rootView.findViewById(R.id.textViewInfoIpValue);
|
||||
// value = SystemUtil.getIPAddress();
|
||||
// view.setText(value);
|
||||
}
|
||||
|
||||
private void netVisibility(View rootView, boolean visible) {
|
||||
int visibility = View.INVISIBLE;
|
||||
if(visible) {
|
||||
visibility = View.VISIBLE;
|
||||
}
|
||||
|
||||
TextView textView = rootView.findViewById(R.id.textViewNetIp);
|
||||
textView.setVisibility(visibility);
|
||||
textView = rootView.findViewById(R.id.textViewNetGateway);
|
||||
textView.setVisibility(visibility);
|
||||
textView = rootView.findViewById(R.id.textViewNetmask);
|
||||
textView.setVisibility(visibility);
|
||||
textView = rootView.findViewById(R.id.textViewNetDns);
|
||||
textView.setVisibility(visibility);
|
||||
|
||||
EditText editText = rootView.findViewById(R.id.editTextNetIpValue);
|
||||
editText.setVisibility(visibility);
|
||||
editText = rootView.findViewById(R.id.editTextNetGatewayValue);
|
||||
editText.setVisibility(visibility);
|
||||
editText = rootView.findViewById(R.id.editTextNetmaskValue);
|
||||
editText.setVisibility(visibility);
|
||||
editText = rootView.findViewById(R.id.editTextNetDnsValue);
|
||||
editText.setVisibility(visibility);
|
||||
}
|
||||
|
||||
private void netUpdate(final View rootView) {
|
||||
//SystemUtil _sysUtil = new SystemUtil();
|
||||
String value = SystemUtil.getConfigValue("ipv4type");
|
||||
|
||||
RadioButton button = rootView.findViewById(R.id.radioButtonStatic);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Log.w("RITO", "STATIC CLICK");
|
||||
netVisibility(rootView, true);
|
||||
}
|
||||
});
|
||||
|
||||
button = rootView.findViewById(R.id.radioButtonDhcp);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Log.w("RITO", "DHCP CLICK");
|
||||
netVisibility(rootView, false);
|
||||
}
|
||||
});
|
||||
|
||||
if(value.equalsIgnoreCase("static")) {
|
||||
RadioButton view = rootView.findViewById(R.id.radioButtonStatic);
|
||||
view.performClick();
|
||||
view.requestFocus();
|
||||
|
||||
EditText editText = rootView.findViewById(R.id.editTextNetIpValue);
|
||||
value = SystemUtil.getConfigValue("ipaddr");
|
||||
editText.setText(value);
|
||||
editText = rootView.findViewById(R.id.editTextNetGatewayValue);
|
||||
value = SystemUtil.getConfigValue("gateway");
|
||||
editText.setText(value);
|
||||
editText = rootView.findViewById(R.id.editTextNetmaskValue);
|
||||
value = SystemUtil.getConfigValue("netmask");
|
||||
editText.setText(value);
|
||||
editText = rootView.findViewById(R.id.editTextNetDnsValue);
|
||||
value = SystemUtil.getConfigValue("dns");
|
||||
editText.setText(value);
|
||||
} else {
|
||||
RadioButton view = rootView.findViewById(R.id.radioButtonDhcp);
|
||||
view.performClick();
|
||||
view.requestFocus();
|
||||
|
||||
//netVisibility(rootView, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void serverUpdate(final View rootView) {
|
||||
//SystemUtil _sysUtil = new SystemUtil();
|
||||
EditText editText = rootView.findViewById(R.id.editTextServerIpValue);
|
||||
String value = SystemUtil.getConfigValue("svraddr");
|
||||
editText.setText(value);
|
||||
|
||||
editText = rootView.findViewById(R.id.editTextServerPortValue);
|
||||
value = SystemUtil.getConfigValue("svrport");
|
||||
if(value.length() == 0)
|
||||
value = "10000";
|
||||
editText.setText(value);
|
||||
}
|
||||
|
||||
private void updateSvrUpdate(final View rootView) {
|
||||
//SystemUtil _sysUtil = new SystemUtil();
|
||||
EditText editText = rootView.findViewById(R.id.editTextUpdateUrlValue);
|
||||
String value = SystemUtil.getConfigValue("updatesvr");
|
||||
editText.setText(value);
|
||||
}
|
||||
|
||||
|
||||
public void saveSettings(String target) {
|
||||
EditText editText;
|
||||
String value;
|
||||
|
||||
if(target.equals("server")) {
|
||||
editText = getView().findViewById(R.id.editTextServerIpValue);
|
||||
value = editText.getText().toString();
|
||||
if (value.length() > 0) {
|
||||
SystemUtil.setConfigValue("svraddr", value);
|
||||
}
|
||||
|
||||
editText = getView().findViewById(R.id.editTextServerPortValue);
|
||||
value = editText.getText().toString();
|
||||
if (value.length() > 0) {
|
||||
SystemUtil.setConfigValue("svrport", value);
|
||||
}
|
||||
|
||||
editText = getView().findViewById(R.id.editTextUpdateUrlValue);
|
||||
value = editText.getText().toString();
|
||||
if (value.length() > 0) {
|
||||
SystemUtil.setConfigValue("updatesvr", value);
|
||||
}
|
||||
}
|
||||
|
||||
if(target.equals("network")) {
|
||||
RadioButton _radio = getView().findViewById(R.id.radioButtonStatic);
|
||||
if (_radio.isChecked()) {
|
||||
editText = getView().findViewById(R.id.editTextNetIpValue);
|
||||
value = editText.getText().toString();
|
||||
if (value.length() > 0) {
|
||||
SystemUtil.setConfigValue("ipaddr", value);
|
||||
}
|
||||
|
||||
editText = getView().findViewById(R.id.editTextNetGatewayValue);
|
||||
value = editText.getText().toString();
|
||||
if (value.length() > 0) {
|
||||
SystemUtil.setConfigValue("gateway", value);
|
||||
}
|
||||
|
||||
editText = getView().findViewById(R.id.editTextNetmaskValue);
|
||||
value = editText.getText().toString();
|
||||
if (value.length() > 0) {
|
||||
SystemUtil.setConfigValue("netmask", value);
|
||||
}
|
||||
|
||||
editText = getView().findViewById(R.id.editTextNetDnsValue);
|
||||
value = editText.getText().toString();
|
||||
if (value.length() > 0) {
|
||||
SystemUtil.setConfigValue("dns", value);
|
||||
}
|
||||
|
||||
SystemUtil.shellCommand("ritosysc NETAPPLY=STATIC");
|
||||
} else {
|
||||
SystemUtil.shellCommand("ritosysc NETAPPLY=DHCP");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
mIpReloadAlive = false;
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
//saveSettings();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View root = inflater.inflate(R.layout.fragment_setting, container, false);
|
||||
final TextView textView = root.findViewById(R.id.section_label);
|
||||
Log.w("RITO", "onCreateView : " + pageViewModel.getIndex());
|
||||
if(pageViewModel.getIndex() == 1) {
|
||||
root.findViewById(R.id.layoutInfo).setVisibility(View.VISIBLE);
|
||||
root.findViewById(R.id.layoutNetwork).setVisibility(View.INVISIBLE);
|
||||
root.findViewById(R.id.layoutServer).setVisibility(View.INVISIBLE);
|
||||
|
||||
infoUpdate(root);
|
||||
} else if(pageViewModel.getIndex() == 2) {
|
||||
root.findViewById(R.id.layoutInfo).setVisibility(View.INVISIBLE);
|
||||
root.findViewById(R.id.layoutNetwork).setVisibility(View.VISIBLE);
|
||||
root.findViewById(R.id.layoutServer).setVisibility(View.INVISIBLE);
|
||||
|
||||
Button button = root.findViewById(R.id.buttonApplyNetwork);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
saveSettings("network");
|
||||
}
|
||||
});
|
||||
netUpdate(root);
|
||||
} else if(pageViewModel.getIndex() == 3) {
|
||||
root.findViewById(R.id.layoutInfo).setVisibility(View.INVISIBLE);
|
||||
root.findViewById(R.id.layoutNetwork).setVisibility(View.INVISIBLE);
|
||||
root.findViewById(R.id.layoutServer).setVisibility(View.VISIBLE);
|
||||
|
||||
Button button = root.findViewById(R.id.buttonApplyServer);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
saveSettings("server");
|
||||
}
|
||||
});
|
||||
serverUpdate(root);
|
||||
updateSvrUpdate(root);
|
||||
}
|
||||
pageViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
|
||||
@Override
|
||||
public void onChanged(@Nullable String s) {
|
||||
textView.setText(s);
|
||||
}
|
||||
});
|
||||
return root;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package kr.co.rito.osicmanager.ui.main;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
|
||||
import kr.co.rito.osicmanager.R;
|
||||
|
||||
/**
|
||||
* A [FragmentPagerAdapter] that returns a fragment corresponding to
|
||||
* one of the sections/tabs/pages.
|
||||
*/
|
||||
public class SectionsPagerAdapter extends FragmentPagerAdapter {
|
||||
|
||||
@StringRes
|
||||
private static final int[] TAB_TITLES = new int[]{R.string.tab_text_1, R.string.tab_text_2, R.string.tab_text_3};
|
||||
private final Context mContext;
|
||||
|
||||
public SectionsPagerAdapter(Context context, FragmentManager fm) {
|
||||
super(fm);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
// getItem is called to instantiate the fragment for the given page.
|
||||
// Return a PlaceholderFragment (defined as a static inner class below).
|
||||
return PlaceholderFragment.newInstance(position + 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return mContext.getResources().getString(TAB_TITLES[position]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
// Show 2 total pages.
|
||||
return 3;
|
||||
}
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*/
|
||||
package org.eclipse.paho.android.service;
|
||||
|
||||
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
|
||||
import org.eclipse.paho.client.mqttv3.IMqttToken;
|
||||
import org.eclipse.paho.client.mqttv3.MqttPingSender;
|
||||
import org.eclipse.paho.client.mqttv3.internal.ClientComms;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Build;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Default ping sender implementation on Android. It is based on AlarmManager.
|
||||
*
|
||||
* <p>This class implements the {@link MqttPingSender} pinger interface
|
||||
* allowing applications to send ping packet to server every keep alive interval.
|
||||
* </p>
|
||||
*
|
||||
* @see MqttPingSender
|
||||
*/
|
||||
class AlarmPingSender implements MqttPingSender {
|
||||
// Identifier for Intents, log messages, etc..
|
||||
private static final String TAG = "AlarmPingSender";
|
||||
|
||||
// TODO: Add log.
|
||||
private ClientComms comms;
|
||||
private MqttService service;
|
||||
private BroadcastReceiver alarmReceiver;
|
||||
private AlarmPingSender that;
|
||||
private PendingIntent pendingIntent;
|
||||
private volatile boolean hasStarted = false;
|
||||
|
||||
public AlarmPingSender(MqttService service) {
|
||||
if (service == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Neither service nor client can be null.");
|
||||
}
|
||||
this.service = service;
|
||||
that = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ClientComms comms) {
|
||||
this.comms = comms;
|
||||
this.alarmReceiver = new AlarmReceiver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
String action = MqttServiceConstants.PING_SENDER
|
||||
+ comms.getClient().getClientId();
|
||||
Log.d(TAG, "Register alarmreceiver to MqttService"+ action);
|
||||
service.registerReceiver(alarmReceiver, new IntentFilter(action));
|
||||
|
||||
pendingIntent = PendingIntent.getBroadcast(service, 0, new Intent(
|
||||
action), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
schedule(comms.getKeepAlive());
|
||||
hasStarted = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
|
||||
Log.d(TAG, "Unregister alarmreceiver to MqttService"+comms.getClient().getClientId());
|
||||
if(hasStarted){
|
||||
if(pendingIntent != null){
|
||||
// Cancel Alarm.
|
||||
AlarmManager alarmManager = (AlarmManager) service.getSystemService(Service.ALARM_SERVICE);
|
||||
alarmManager.cancel(pendingIntent);
|
||||
}
|
||||
|
||||
hasStarted = false;
|
||||
try{
|
||||
service.unregisterReceiver(alarmReceiver);
|
||||
}catch(IllegalArgumentException e){
|
||||
//Ignore unregister errors.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void schedule(long delayInMilliseconds) {
|
||||
long nextAlarmInMilliseconds = System.currentTimeMillis()
|
||||
+ delayInMilliseconds;
|
||||
Log.d(TAG, "Schedule next alarm at " + nextAlarmInMilliseconds);
|
||||
AlarmManager alarmManager = (AlarmManager) service
|
||||
.getSystemService(Service.ALARM_SERVICE);
|
||||
|
||||
if(Build.VERSION.SDK_INT >= 23){
|
||||
// In SDK 23 and above, dosing will prevent setExact, setExactAndAllowWhileIdle will force
|
||||
// the device to run this task whilst dosing.
|
||||
Log.d(TAG, "Alarm scheule using setExactAndAllowWhileIdle, next: " + delayInMilliseconds);
|
||||
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextAlarmInMilliseconds,
|
||||
pendingIntent);
|
||||
} else if (Build.VERSION.SDK_INT >= 19) {
|
||||
Log.d(TAG, "Alarm scheule using setExact, delay: " + delayInMilliseconds);
|
||||
alarmManager.setExact(AlarmManager.RTC_WAKEUP, nextAlarmInMilliseconds,
|
||||
pendingIntent);
|
||||
} else {
|
||||
alarmManager.set(AlarmManager.RTC_WAKEUP, nextAlarmInMilliseconds,
|
||||
pendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This class sends PingReq packet to MQTT broker
|
||||
*/
|
||||
class AlarmReceiver extends BroadcastReceiver {
|
||||
private WakeLock wakelock;
|
||||
private final String wakeLockTag = MqttServiceConstants.PING_WAKELOCK
|
||||
+ that.comms.getClient().getClientId();
|
||||
|
||||
@Override
|
||||
@SuppressLint("Wakelock")
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
// According to the docs, "Alarm Manager holds a CPU wake lock as
|
||||
// long as the alarm receiver's onReceive() method is executing.
|
||||
// This guarantees that the phone will not sleep until you have
|
||||
// finished handling the broadcast.", but this class still get
|
||||
// a wake lock to wait for ping finished.
|
||||
|
||||
Log.d(TAG, "Sending Ping at:" + System.currentTimeMillis());
|
||||
|
||||
PowerManager pm = (PowerManager) service
|
||||
.getSystemService(Service.POWER_SERVICE);
|
||||
wakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakeLockTag);
|
||||
wakelock.acquire();
|
||||
|
||||
// Assign new callback to token to execute code after PingResq
|
||||
// arrives. Get another wakelock even receiver already has one,
|
||||
// release it until ping response returns.
|
||||
IMqttToken token = comms.checkForActivity(new IMqttActionListener() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(IMqttToken asyncActionToken) {
|
||||
Log.d(TAG, "Success. Release lock(" + wakeLockTag + "):"
|
||||
+ System.currentTimeMillis());
|
||||
//Release wakelock when it is done.
|
||||
wakelock.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(IMqttToken asyncActionToken,
|
||||
Throwable exception) {
|
||||
Log.d(TAG, "Failure. Release lock(" + wakeLockTag + "):"
|
||||
+ System.currentTimeMillis());
|
||||
//Release wakelock when it is done.
|
||||
wakelock.release();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (token == null && wakelock.isHeld()) {
|
||||
wakelock.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,462 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 1999, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* James Sutton - Removing SQL Injection vunerability (bug 467378)
|
||||
*/
|
||||
package org.eclipse.paho.android.service;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.SQLException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link MessageStore} interface, using a SQLite database
|
||||
*
|
||||
*/
|
||||
class DatabaseMessageStore implements MessageStore {
|
||||
|
||||
// TAG used for indentify trace data etc.
|
||||
private static final String TAG = "DatabaseMessageStore";
|
||||
|
||||
// One "private" database column name
|
||||
// The other database column names are defined in MqttServiceConstants
|
||||
private static final String MTIMESTAMP = "mtimestamp";
|
||||
|
||||
// the name of the table in the database to which we will save messages
|
||||
private static final String ARRIVED_MESSAGE_TABLE_NAME = "MqttArrivedMessageTable";
|
||||
|
||||
// the database
|
||||
private SQLiteDatabase db = null;
|
||||
|
||||
// a SQLiteOpenHelper specific for this database
|
||||
private MQTTDatabaseHelper mqttDb = null;
|
||||
|
||||
// a place to send trace data
|
||||
private MqttTraceHandler traceHandler = null;
|
||||
|
||||
/**
|
||||
* We need a SQLiteOpenHelper to handle database creation and updating
|
||||
*
|
||||
*/
|
||||
private static class MQTTDatabaseHelper extends SQLiteOpenHelper {
|
||||
// TAG used for indentify trace data etc.
|
||||
private static final String TAG = "MQTTDatabaseHelper";
|
||||
|
||||
private static final String DATABASE_NAME = "mqttAndroidService.db";
|
||||
|
||||
// database version, used to recognise when we need to upgrade
|
||||
// (delete and recreate)
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
|
||||
// a place to send trace data
|
||||
private MqttTraceHandler traceHandler = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param traceHandler
|
||||
* @param context
|
||||
*/
|
||||
public MQTTDatabaseHelper(MqttTraceHandler traceHandler, Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
this.traceHandler = traceHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* When the database is (re)created, create our table
|
||||
*
|
||||
* @param database
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase database) {
|
||||
String createArrivedTableStatement = "CREATE TABLE "
|
||||
+ ARRIVED_MESSAGE_TABLE_NAME + "("
|
||||
+ MqttServiceConstants.MESSAGE_ID + " TEXT PRIMARY KEY, "
|
||||
+ MqttServiceConstants.CLIENT_HANDLE + " TEXT, "
|
||||
+ MqttServiceConstants.DESTINATION_NAME + " TEXT, "
|
||||
+ MqttServiceConstants.PAYLOAD + " BLOB, "
|
||||
+ MqttServiceConstants.QOS + " INTEGER, "
|
||||
+ MqttServiceConstants.RETAINED + " TEXT, "
|
||||
+ MqttServiceConstants.DUPLICATE + " TEXT, " + MTIMESTAMP
|
||||
+ " INTEGER" + ");";
|
||||
traceHandler.traceDebug(TAG, "onCreate {"
|
||||
+ createArrivedTableStatement + "}");
|
||||
try {
|
||||
database.execSQL(createArrivedTableStatement);
|
||||
traceHandler.traceDebug(TAG, "created the table");
|
||||
} catch (SQLException e) {
|
||||
traceHandler.traceException(TAG, "onCreate", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To upgrade the database, drop and recreate our table
|
||||
*
|
||||
* @param db
|
||||
* the database
|
||||
* @param oldVersion
|
||||
* ignored
|
||||
* @param newVersion
|
||||
* ignored
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
traceHandler.traceDebug(TAG, "onUpgrade");
|
||||
try {
|
||||
db.execSQL("DROP TABLE IF EXISTS " + ARRIVED_MESSAGE_TABLE_NAME);
|
||||
} catch (SQLException e) {
|
||||
traceHandler.traceException(TAG, "onUpgrade", e);
|
||||
throw e;
|
||||
}
|
||||
onCreate(db);
|
||||
traceHandler.traceDebug(TAG, "onUpgrade complete");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor - create a DatabaseMessageStore to store arrived MQTT message
|
||||
*
|
||||
* @param service
|
||||
* our parent MqttService
|
||||
* @param context
|
||||
* a context to use for android calls
|
||||
*/
|
||||
public DatabaseMessageStore(MqttService service, Context context) {
|
||||
this.traceHandler = service;
|
||||
|
||||
// Open message database
|
||||
mqttDb = new MQTTDatabaseHelper(traceHandler, context);
|
||||
|
||||
// Android documentation suggests that this perhaps
|
||||
// could/should be done in another thread, but as the
|
||||
// database is only one table, I doubt it matters...
|
||||
|
||||
traceHandler.traceDebug(TAG, "DatabaseMessageStore<init> complete");
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an MQTT message
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifier for the client storing the message
|
||||
* @param topic
|
||||
* The topic on which the message was published
|
||||
* @param message
|
||||
* the arrived MQTT message
|
||||
* @return an identifier for the message, so that it can be removed when appropriate
|
||||
*/
|
||||
@Override
|
||||
public String storeArrived(String clientHandle, String topic,
|
||||
MqttMessage message) {
|
||||
|
||||
db = mqttDb.getWritableDatabase();
|
||||
|
||||
traceHandler.traceDebug(TAG, "storeArrived{" + clientHandle + "}, {"
|
||||
+ message.toString() + "}");
|
||||
|
||||
byte[] payload = message.getPayload();
|
||||
int qos = message.getQos();
|
||||
boolean retained = message.isRetained();
|
||||
boolean duplicate = message.isDuplicate();
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
String id = java.util.UUID.randomUUID().toString();
|
||||
values.put(MqttServiceConstants.MESSAGE_ID, id);
|
||||
values.put(MqttServiceConstants.CLIENT_HANDLE, clientHandle);
|
||||
values.put(MqttServiceConstants.DESTINATION_NAME, topic);
|
||||
values.put(MqttServiceConstants.PAYLOAD, payload);
|
||||
values.put(MqttServiceConstants.QOS, qos);
|
||||
values.put(MqttServiceConstants.RETAINED, retained);
|
||||
values.put(MqttServiceConstants.DUPLICATE, duplicate);
|
||||
values.put(MTIMESTAMP, System.currentTimeMillis());
|
||||
try {
|
||||
db.insertOrThrow(ARRIVED_MESSAGE_TABLE_NAME, null, values);
|
||||
} catch (SQLException e) {
|
||||
traceHandler.traceException(TAG, "onUpgrade", e);
|
||||
throw e;
|
||||
}
|
||||
int count = getArrivedRowCount(clientHandle);
|
||||
traceHandler
|
||||
.traceDebug(
|
||||
TAG,
|
||||
"storeArrived: inserted message with id of {"
|
||||
+ id
|
||||
+ "} - Number of messages in database for this clientHandle = "
|
||||
+ count);
|
||||
return id;
|
||||
}
|
||||
|
||||
private int getArrivedRowCount(String clientHandle) {
|
||||
int count = 0;
|
||||
String[] projection = {
|
||||
MqttServiceConstants.MESSAGE_ID,
|
||||
};
|
||||
String selection = MqttServiceConstants.CLIENT_HANDLE + "=?";
|
||||
String[] selectionArgs = new String[1];
|
||||
selectionArgs[0] = clientHandle;
|
||||
Cursor c = db.query(
|
||||
ARRIVED_MESSAGE_TABLE_NAME, // Table Name
|
||||
projection, // The columns to return;
|
||||
selection, // Columns for WHERE Clause
|
||||
selectionArgs , // The values for the WHERE Cause
|
||||
null, //Don't group the rows
|
||||
null, // Don't filter by row groups
|
||||
null // The sort order
|
||||
);
|
||||
|
||||
if (c.moveToFirst()) {
|
||||
count = c.getInt(0);
|
||||
}
|
||||
c.close();
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an MQTT message.
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifier for the client which stored the message
|
||||
* @param id
|
||||
* the identifying string returned when the message was stored
|
||||
*
|
||||
* @return true if the message was found and deleted
|
||||
*/
|
||||
@Override
|
||||
public boolean discardArrived(String clientHandle, String id) {
|
||||
|
||||
db = mqttDb.getWritableDatabase();
|
||||
|
||||
traceHandler.traceDebug(TAG, "discardArrived{" + clientHandle + "}, {"
|
||||
+ id + "}");
|
||||
int rows;
|
||||
String[] selectionArgs = new String[2];
|
||||
selectionArgs[0] = id;
|
||||
selectionArgs[1] = clientHandle;
|
||||
|
||||
try {
|
||||
rows = db.delete(ARRIVED_MESSAGE_TABLE_NAME,
|
||||
MqttServiceConstants.MESSAGE_ID + "=? AND "
|
||||
+ MqttServiceConstants.CLIENT_HANDLE + "=?",
|
||||
selectionArgs);
|
||||
} catch (SQLException e) {
|
||||
traceHandler.traceException(TAG, "discardArrived", e);
|
||||
throw e;
|
||||
}
|
||||
if (rows != 1) {
|
||||
traceHandler.traceError(TAG,
|
||||
"discardArrived - Error deleting message {" + id
|
||||
+ "} from database: Rows affected = " + rows);
|
||||
return false;
|
||||
}
|
||||
int count = getArrivedRowCount(clientHandle);
|
||||
traceHandler
|
||||
.traceDebug(
|
||||
TAG,
|
||||
"discardArrived - Message deleted successfully. - messages in db for this clientHandle "
|
||||
+ count);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator over all messages stored (optionally for a specific client)
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifier for the client.<br>
|
||||
* If null, all messages are retrieved
|
||||
* @return iterator of all the arrived MQTT messages
|
||||
*/
|
||||
@Override
|
||||
public Iterator<StoredMessage> getAllArrivedMessages(
|
||||
final String clientHandle) {
|
||||
return new Iterator<StoredMessage>() {
|
||||
private Cursor c;
|
||||
private boolean hasNext;
|
||||
private final String[] selectionArgs = {
|
||||
clientHandle,
|
||||
};
|
||||
|
||||
|
||||
{
|
||||
db = mqttDb.getWritableDatabase();
|
||||
// anonymous initialiser to start a suitable query
|
||||
// and position at the first row, if one exists
|
||||
if (clientHandle == null) {
|
||||
c = db.query(ARRIVED_MESSAGE_TABLE_NAME,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"mtimestamp ASC");
|
||||
} else {
|
||||
c = db.query(ARRIVED_MESSAGE_TABLE_NAME,
|
||||
null,
|
||||
MqttServiceConstants.CLIENT_HANDLE + "=?",
|
||||
selectionArgs,
|
||||
null,
|
||||
null,
|
||||
"mtimestamp ASC");
|
||||
}
|
||||
hasNext = c.moveToFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (!hasNext){
|
||||
c.close();
|
||||
}
|
||||
return hasNext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredMessage next() {
|
||||
String messageId = c.getString(c
|
||||
.getColumnIndex(MqttServiceConstants.MESSAGE_ID));
|
||||
String clientHandle = c.getString(c
|
||||
.getColumnIndex(MqttServiceConstants.CLIENT_HANDLE));
|
||||
String topic = c.getString(c
|
||||
.getColumnIndex(MqttServiceConstants.DESTINATION_NAME));
|
||||
byte[] payload = c.getBlob(c
|
||||
.getColumnIndex(MqttServiceConstants.PAYLOAD));
|
||||
int qos = c.getInt(c.getColumnIndex(MqttServiceConstants.QOS));
|
||||
boolean retained = Boolean.parseBoolean(c.getString(c
|
||||
.getColumnIndex(MqttServiceConstants.RETAINED)));
|
||||
boolean dup = Boolean.parseBoolean(c.getString(c
|
||||
.getColumnIndex(MqttServiceConstants.DUPLICATE)));
|
||||
|
||||
// build the result
|
||||
MqttMessageHack message = new MqttMessageHack(payload);
|
||||
message.setQos(qos);
|
||||
message.setRetained(retained);
|
||||
message.setDuplicate(dup);
|
||||
|
||||
// move on
|
||||
hasNext = c.moveToNext();
|
||||
return new DbStoredData(messageId, clientHandle, topic, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#finalize()
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
c.close();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all messages (optionally for a specific client)
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifier for the client.<br>
|
||||
* If null, all messages are deleted
|
||||
*/
|
||||
@Override
|
||||
public void clearArrivedMessages(String clientHandle) {
|
||||
|
||||
db = mqttDb.getWritableDatabase();
|
||||
String[] selectionArgs = new String[1];
|
||||
selectionArgs[0] = clientHandle;
|
||||
|
||||
int rows = 0;
|
||||
if (clientHandle == null) {
|
||||
traceHandler.traceDebug(TAG,
|
||||
"clearArrivedMessages: clearing the table");
|
||||
rows = db.delete(ARRIVED_MESSAGE_TABLE_NAME, null, null);
|
||||
} else {
|
||||
traceHandler.traceDebug(TAG,
|
||||
"clearArrivedMessages: clearing the table of "
|
||||
+ clientHandle + " messages");
|
||||
rows = db.delete(ARRIVED_MESSAGE_TABLE_NAME,
|
||||
MqttServiceConstants.CLIENT_HANDLE + "=?",
|
||||
selectionArgs);
|
||||
|
||||
}
|
||||
traceHandler.traceDebug(TAG, "clearArrivedMessages: rows affected = "
|
||||
+ rows);
|
||||
}
|
||||
|
||||
private class DbStoredData implements StoredMessage {
|
||||
private String messageId;
|
||||
private String clientHandle;
|
||||
private String topic;
|
||||
private MqttMessage message;
|
||||
|
||||
DbStoredData(String messageId, String clientHandle, String topic,
|
||||
MqttMessage message) {
|
||||
this.messageId = messageId;
|
||||
this.topic = topic;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientHandle() {
|
||||
return clientHandle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttMessage getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A way to get at the "setDuplicate" method of MqttMessage
|
||||
*/
|
||||
private class MqttMessageHack extends MqttMessage {
|
||||
|
||||
public MqttMessageHack(byte[] payload) {
|
||||
super(payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDuplicate(boolean dup) {
|
||||
super.setDuplicate(dup);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.db!=null)
|
||||
this.db.close();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 1999, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*/
|
||||
package org.eclipse.paho.android.service;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mechanism for persisting messages until we know they have been received
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>A Service should store messages as they arrive via
|
||||
* {@link #storeArrived(String, String, MqttMessage)}.
|
||||
* <li>When a message has been passed to the consuming entity,
|
||||
* {@link #discardArrived(String, String)} should be called.
|
||||
* <li>To recover messages which have not been definitely passed to the
|
||||
* consumer, {@link MessageStore#getAllArrivedMessages(String)} is used.
|
||||
* <li>When a clean session is started {@link #clearArrivedMessages(String)} is
|
||||
* used.
|
||||
* </ul>
|
||||
*/
|
||||
interface MessageStore {
|
||||
|
||||
/**
|
||||
* External representation of a stored message
|
||||
*/
|
||||
interface StoredMessage {
|
||||
/**
|
||||
* @return the identifier for the message within the store
|
||||
*/
|
||||
String getMessageId();
|
||||
|
||||
/**
|
||||
* @return the identifier of the client which stored this message
|
||||
*/
|
||||
String getClientHandle();
|
||||
|
||||
/**
|
||||
* @return the topic on which the message was received
|
||||
*/
|
||||
String getTopic();
|
||||
|
||||
/**
|
||||
* @return the identifier of the client which stored this message
|
||||
*/
|
||||
MqttMessage getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a message and return an identifier for it
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifier for the client
|
||||
* @param message
|
||||
* message to be stored
|
||||
* @return a unique identifier for it
|
||||
*/
|
||||
String storeArrived(String clientHandle, String Topic,
|
||||
MqttMessage message);
|
||||
|
||||
/**
|
||||
* Discard a message - called when we are certain that an arrived message
|
||||
* has reached the application.
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifier for the client
|
||||
* @param id
|
||||
* id of message to be discarded
|
||||
*/
|
||||
boolean discardArrived(String clientHandle, String id);
|
||||
|
||||
/**
|
||||
* Get all the stored messages, usually for a specific client
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifier for the client - if null, then messages for all
|
||||
* clients are returned
|
||||
*/
|
||||
Iterator<StoredMessage> getAllArrivedMessages(String clientHandle);
|
||||
|
||||
/**
|
||||
* Discard stored messages, usually for a specific client
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifier for the client - if null, then messages for all
|
||||
* clients are discarded
|
||||
*/
|
||||
void clearArrivedMessages(String clientHandle);
|
||||
|
||||
void close();
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 1999, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*/
|
||||
package org.eclipse.paho.android.service;
|
||||
|
||||
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
|
||||
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
|
||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Implementation of the IMqttDeliveryToken interface for use from within the
|
||||
* MqttAndroidClient implementation
|
||||
*/
|
||||
class MqttDeliveryTokenAndroid extends MqttTokenAndroid
|
||||
implements IMqttDeliveryToken {
|
||||
|
||||
// The message which is being tracked by this token
|
||||
private MqttMessage message;
|
||||
|
||||
MqttDeliveryTokenAndroid(MqttAndroidClient client,
|
||||
Object userContext, IMqttActionListener listener, MqttMessage message) {
|
||||
super(client, userContext, listener);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.paho.client.mqttv3.IMqttDeliveryToken#getMessage()
|
||||
*/
|
||||
@Override
|
||||
public MqttMessage getMessage() throws MqttException {
|
||||
return message;
|
||||
}
|
||||
|
||||
void setMessage(MqttMessage message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
void notifyDelivery(MqttMessage delivered) {
|
||||
message = delivered;
|
||||
super.notifyComplete();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,912 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 1999, 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* James Sutton - isOnline Null Pointer (bug 473775)
|
||||
*/
|
||||
package org.eclipse.paho.android.service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.paho.client.mqttv3.DisconnectedBufferOptions;
|
||||
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
|
||||
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
|
||||
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
|
||||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
|
||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||||
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
|
||||
import org.eclipse.paho.client.mqttv3.MqttSecurityException;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The android service which interfaces with an MQTT client implementation
|
||||
* </p>
|
||||
* <p>
|
||||
* The main API of MqttService is intended to pretty much mirror the
|
||||
* IMqttAsyncClient with appropriate adjustments for the Android environment.<br>
|
||||
* These adjustments usually consist of adding two parameters to each method :-
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>invocationContext - a string passed from the application to identify the
|
||||
* context of the operation (mainly included for support of the javascript API
|
||||
* implementation)</li>
|
||||
* <li>activityToken - a string passed from the Activity to relate back to a
|
||||
* callback method or other context-specific data</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* To support multiple client connections, the bulk of the MQTT work is
|
||||
* delegated to MqttConnection objects. These are identified by "client
|
||||
* handle" strings, which is how the Activity, and the higher-level APIs refer
|
||||
* to them.
|
||||
* </p>
|
||||
* <p>
|
||||
* Activities using this service are expected to start it and bind to it using
|
||||
* the BIND_AUTO_CREATE flag. The life cycle of this service is based on this
|
||||
* approach.
|
||||
* </p>
|
||||
* <p>
|
||||
* Operations are highly asynchronous - in most cases results are returned to
|
||||
* the Activity by broadcasting one (or occasionally more) appropriate Intents,
|
||||
* which the Activity is expected to register a listener for.<br>
|
||||
* The Intents have an Action of
|
||||
* {@link MqttServiceConstants#CALLBACK_TO_ACTIVITY
|
||||
* MqttServiceConstants.CALLBACK_TO_ACTIVITY} which allows the Activity to
|
||||
* register a listener with an appropriate IntentFilter.<br>
|
||||
* Further data is provided by "Extra Data" in the Intent, as follows :-
|
||||
* </p>
|
||||
* <table border="1" summary="">
|
||||
* <tr>
|
||||
* <th align="left">Name</th>
|
||||
* <th align="left">Data Type</th>
|
||||
* <th align="left">Value</th>
|
||||
* <th align="left">Operations used for</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">
|
||||
* {@link MqttServiceConstants#CALLBACK_CLIENT_HANDLE
|
||||
* MqttServiceConstants.CALLBACK_CLIENT_HANDLE}</td>
|
||||
* <td align="left" valign="top">String</td>
|
||||
* <td align="left" valign="top">The clientHandle identifying the client which
|
||||
* initiated this operation</td>
|
||||
* <td align="left" valign="top">All operations</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">{@link MqttServiceConstants#CALLBACK_STATUS
|
||||
* MqttServiceConstants.CALLBACK_STATUS}</td>
|
||||
* <td align="left" valign="top">Serializable</td>
|
||||
* <td align="left" valign="top">An {@link Status} value indicating success or
|
||||
* otherwise of the operation</td>
|
||||
* <td align="left" valign="top">All operations</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">
|
||||
* {@link MqttServiceConstants#CALLBACK_ACTIVITY_TOKEN
|
||||
* MqttServiceConstants.CALLBACK_ACTIVITY_TOKEN}</td>
|
||||
* <td align="left" valign="top">String</td>
|
||||
* <td align="left" valign="top">the activityToken passed into the operation</td>
|
||||
* <td align="left" valign="top">All operations</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">
|
||||
* {@link MqttServiceConstants#CALLBACK_INVOCATION_CONTEXT
|
||||
* MqttServiceConstants.CALLBACK_INVOCATION_CONTEXT}</td>
|
||||
* <td align="left" valign="top">String</td>
|
||||
* <td align="left" valign="top">the invocationContext passed into the operation
|
||||
* </td>
|
||||
* <td align="left" valign="top">All operations</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">{@link MqttServiceConstants#CALLBACK_ACTION
|
||||
* MqttServiceConstants.CALLBACK_ACTION}</td>
|
||||
* <td align="left" valign="top">String</td>
|
||||
* <td align="left" valign="top">one of
|
||||
* <table summary="">
|
||||
* <tr>
|
||||
* <td align="left" valign="top"> {@link MqttServiceConstants#SEND_ACTION
|
||||
* MqttServiceConstants.SEND_ACTION}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">
|
||||
* {@link MqttServiceConstants#UNSUBSCRIBE_ACTION
|
||||
* MqttServiceConstants.UNSUBSCRIBE_ACTION}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top"> {@link MqttServiceConstants#SUBSCRIBE_ACTION
|
||||
* MqttServiceConstants.SUBSCRIBE_ACTION}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top"> {@link MqttServiceConstants#DISCONNECT_ACTION
|
||||
* MqttServiceConstants.DISCONNECT_ACTION}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top"> {@link MqttServiceConstants#CONNECT_ACTION
|
||||
* MqttServiceConstants.CONNECT_ACTION}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">
|
||||
* {@link MqttServiceConstants#MESSAGE_ARRIVED_ACTION
|
||||
* MqttServiceConstants.MESSAGE_ARRIVED_ACTION}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">
|
||||
* {@link MqttServiceConstants#MESSAGE_DELIVERED_ACTION
|
||||
* MqttServiceConstants.MESSAGE_DELIVERED_ACTION}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">
|
||||
* {@link MqttServiceConstants#ON_CONNECTION_LOST_ACTION
|
||||
* MqttServiceConstants.ON_CONNECTION_LOST_ACTION}</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* </td>
|
||||
* <td align="left" valign="top">All operations</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">
|
||||
* {@link MqttServiceConstants#CALLBACK_ERROR_MESSAGE
|
||||
* MqttServiceConstants.CALLBACK_ERROR_MESSAGE}
|
||||
* <td align="left" valign="top">String</td>
|
||||
* <td align="left" valign="top">A suitable error message (taken from the
|
||||
* relevant exception where possible)</td>
|
||||
* <td align="left" valign="top">All failing operations</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">
|
||||
* {@link MqttServiceConstants#CALLBACK_ERROR_NUMBER
|
||||
* MqttServiceConstants.CALLBACK_ERROR_NUMBER}
|
||||
* <td align="left" valign="top">int</td>
|
||||
* <td align="left" valign="top">A suitable error code (taken from the relevant
|
||||
* exception where possible)</td>
|
||||
* <td align="left" valign="top">All failing operations</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">
|
||||
* {@link MqttServiceConstants#CALLBACK_EXCEPTION_STACK
|
||||
* MqttServiceConstants.CALLBACK_EXCEPTION_STACK}</td>
|
||||
* <td align="left" valign="top">String</td>
|
||||
* <td align="left" valign="top">The stacktrace of the failing call</td>
|
||||
* <td align="left" valign="top">The Connection Lost event</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">
|
||||
* {@link MqttServiceConstants#CALLBACK_MESSAGE_ID
|
||||
* MqttServiceConstants.CALLBACK_MESSAGE_ID}</td>
|
||||
* <td align="left" valign="top">String</td>
|
||||
* <td align="left" valign="top">The identifier for the message in the message
|
||||
* store, used by the Activity to acknowledge the arrival of the message, so
|
||||
* that the service may remove it from the store</td>
|
||||
* <td align="left" valign="top">The Message Arrived event</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">
|
||||
* {@link MqttServiceConstants#CALLBACK_DESTINATION_NAME
|
||||
* MqttServiceConstants.CALLBACK_DESTINATION_NAME}
|
||||
* <td align="left" valign="top">String</td>
|
||||
* <td align="left" valign="top">The topic on which the message was received</td>
|
||||
* <td align="left" valign="top">The Message Arrived event</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td align="left" valign="top">
|
||||
* {@link MqttServiceConstants#CALLBACK_MESSAGE_PARCEL
|
||||
* MqttServiceConstants.CALLBACK_MESSAGE_PARCEL}</td>
|
||||
* <td align="left" valign="top">Parcelable</td>
|
||||
* <td align="left" valign="top">The new message encapsulated in Android
|
||||
* Parcelable format as a {@link ParcelableMqttMessage}</td>
|
||||
* <td align="left" valign="top">The Message Arrived event</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*/
|
||||
@SuppressLint("Registered")
|
||||
public class MqttService extends Service implements MqttTraceHandler {
|
||||
|
||||
// Identifier for Intents, log messages, etc..
|
||||
static final String TAG = "MqttService";
|
||||
|
||||
// callback id for making trace callbacks to the Activity
|
||||
// needs to be set by the activity as appropriate
|
||||
private String traceCallbackId;
|
||||
// state of tracing
|
||||
private boolean traceEnabled = false;
|
||||
|
||||
// somewhere to persist received messages until we're sure
|
||||
// that they've reached the application
|
||||
MessageStore messageStore;
|
||||
|
||||
// An intent receiver to deal with changes in network connectivity
|
||||
private NetworkConnectionIntentReceiver networkConnectionMonitor;
|
||||
|
||||
//a receiver to recognise when the user changes the "background data" preference
|
||||
// and a flag to track that preference
|
||||
// Only really relevant below android version ICE_CREAM_SANDWICH - see
|
||||
// android docs
|
||||
private BackgroundDataPreferenceReceiver backgroundDataPreferenceMonitor;
|
||||
private volatile boolean backgroundDataEnabled = true;
|
||||
|
||||
// a way to pass ourself back to the activity
|
||||
private MqttServiceBinder mqttServiceBinder;
|
||||
|
||||
// mapping from client handle strings to actual client connections.
|
||||
private Map<String/* clientHandle */, MqttConnection/* client */> connections = new ConcurrentHashMap<>();
|
||||
|
||||
public MqttService() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* pass data back to the Activity, by building a suitable Intent object and
|
||||
* broadcasting it
|
||||
*
|
||||
* @param clientHandle
|
||||
* source of the data
|
||||
* @param status
|
||||
* OK or Error
|
||||
* @param dataBundle
|
||||
* the data to be passed
|
||||
*/
|
||||
void callbackToActivity(String clientHandle, Status status,
|
||||
Bundle dataBundle) {
|
||||
// Don't call traceDebug, as it will try to callbackToActivity leading
|
||||
// to recursion.
|
||||
Intent callbackIntent = new Intent(
|
||||
MqttServiceConstants.CALLBACK_TO_ACTIVITY);
|
||||
if (clientHandle != null) {
|
||||
callbackIntent.putExtra(
|
||||
MqttServiceConstants.CALLBACK_CLIENT_HANDLE, clientHandle);
|
||||
}
|
||||
callbackIntent.putExtra(MqttServiceConstants.CALLBACK_STATUS, status);
|
||||
if (dataBundle != null) {
|
||||
callbackIntent.putExtras(dataBundle);
|
||||
}
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(callbackIntent);
|
||||
}
|
||||
|
||||
// The major API implementation follows :-
|
||||
|
||||
/**
|
||||
* Get an MqttConnection object to represent a connection to a server
|
||||
*
|
||||
* @param serverURI specifies the protocol, host name and port to be used to connect to an MQTT server
|
||||
* @param clientId specifies the name by which this connection should be identified to the server
|
||||
* @param contextId specifies the app conext info to make a difference between apps
|
||||
* @param persistence specifies the persistence layer to be used with this client
|
||||
* @return a string to be used by the Activity as a "handle" for this
|
||||
* MqttConnection
|
||||
*/
|
||||
public String getClient(String serverURI, String clientId, String contextId, MqttClientPersistence persistence) {
|
||||
String clientHandle = serverURI + ":" + clientId+":"+contextId;
|
||||
if (!connections.containsKey(clientHandle)) {
|
||||
MqttConnection client = new MqttConnection(this, serverURI,
|
||||
clientId, persistence, clientHandle);
|
||||
connections.put(clientHandle, client);
|
||||
}
|
||||
return clientHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the MQTT server specified by a particular client
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifies the MqttConnection to use
|
||||
* @param connectOptions
|
||||
* the MQTT connection options to be used
|
||||
* @param invocationContext
|
||||
* arbitrary data to be passed back to the application
|
||||
* @param activityToken
|
||||
* arbitrary identifier to be passed back to the Activity
|
||||
* @throws MqttSecurityException thrown if there is a security exception
|
||||
* @throws MqttException thrown for all other MqttExceptions
|
||||
*/
|
||||
public void connect(String clientHandle, MqttConnectOptions connectOptions,
|
||||
String invocationContext, String activityToken)
|
||||
throws MqttSecurityException, MqttException {
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
client.connect(connectOptions, null, activityToken);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Request all clients to reconnect if appropriate
|
||||
*/
|
||||
void reconnect() {
|
||||
traceDebug(TAG, "Reconnect to server, client size=" + connections.size());
|
||||
for (MqttConnection client : connections.values()) {
|
||||
traceDebug("Reconnect Client:",
|
||||
client.getClientId() + '/' + client.getServerURI());
|
||||
if(this.isOnline()){
|
||||
client.reconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close connection from a particular client
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifies the MqttConnection to use
|
||||
*/
|
||||
public void close(String clientHandle) {
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
client.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the server
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifies the MqttConnection to use
|
||||
* @param invocationContext
|
||||
* arbitrary data to be passed back to the application
|
||||
* @param activityToken
|
||||
* arbitrary identifier to be passed back to the Activity
|
||||
*/
|
||||
public void disconnect(String clientHandle, String invocationContext,
|
||||
String activityToken) {
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
client.disconnect(invocationContext, activityToken);
|
||||
connections.remove(clientHandle);
|
||||
|
||||
|
||||
// the activity has finished using us, so we can stop the service
|
||||
// the activities are bound with BIND_AUTO_CREATE, so the service will
|
||||
// remain around until the last activity disconnects
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the server
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifies the MqttConnection to use
|
||||
* @param quiesceTimeout
|
||||
* in milliseconds
|
||||
* @param invocationContext
|
||||
* arbitrary data to be passed back to the application
|
||||
* @param activityToken
|
||||
* arbitrary identifier to be passed back to the Activity
|
||||
*/
|
||||
public void disconnect(String clientHandle, long quiesceTimeout,
|
||||
String invocationContext, String activityToken) {
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
client.disconnect(quiesceTimeout, invocationContext, activityToken);
|
||||
connections.remove(clientHandle);
|
||||
|
||||
// the activity has finished using us, so we can stop the service
|
||||
// the activities are bound with BIND_AUTO_CREATE, so the service will
|
||||
// remain around until the last activity disconnects
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status of a specific client
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifies the MqttConnection to use
|
||||
* @return true if the specified client is connected to an MQTT server
|
||||
*/
|
||||
public boolean isConnected(String clientHandle) {
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
return client.isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish a message to a topic
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifies the MqttConnection to use
|
||||
* @param topic
|
||||
* the topic to which to publish
|
||||
* @param payload
|
||||
* the content of the message to publish
|
||||
* @param qos
|
||||
* the quality of service requested
|
||||
* @param retained
|
||||
* whether the MQTT server should retain this message
|
||||
* @param invocationContext
|
||||
* arbitrary data to be passed back to the application
|
||||
* @param activityToken
|
||||
* arbitrary identifier to be passed back to the Activity
|
||||
* @throws MqttPersistenceException when a problem occurs storing the message
|
||||
* @throws MqttException if there was an error publishing the message
|
||||
* @return token for tracking the operation
|
||||
*/
|
||||
public IMqttDeliveryToken publish(String clientHandle, String topic,
|
||||
byte[] payload, int qos, boolean retained,
|
||||
String invocationContext, String activityToken)
|
||||
throws MqttPersistenceException, MqttException {
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
return client.publish(topic, payload, qos, retained, invocationContext,
|
||||
activityToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish a message to a topic
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifies the MqttConnection to use
|
||||
* @param topic
|
||||
* the topic to which to publish
|
||||
* @param message
|
||||
* the message to publish
|
||||
* @param invocationContext
|
||||
* arbitrary data to be passed back to the application
|
||||
* @param activityToken
|
||||
* arbitrary identifier to be passed back to the Activity
|
||||
* @throws MqttPersistenceException when a problem occurs storing the message
|
||||
* @throws MqttException if there was an error publishing the message
|
||||
* @return token for tracking the operation
|
||||
*/
|
||||
public IMqttDeliveryToken publish(String clientHandle, String topic,
|
||||
MqttMessage message, String invocationContext, String activityToken)
|
||||
throws MqttPersistenceException, MqttException {
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
return client.publish(topic, message, invocationContext, activityToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a topic
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifies the MqttConnection to use
|
||||
* @param topic
|
||||
* a possibly wildcarded topic name
|
||||
* @param qos
|
||||
* requested quality of service for the topic
|
||||
* @param invocationContext
|
||||
* arbitrary data to be passed back to the application
|
||||
* @param activityToken
|
||||
* arbitrary identifier to be passed back to the Activity
|
||||
*/
|
||||
public void subscribe(String clientHandle, String topic, int qos,
|
||||
String invocationContext, String activityToken) {
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
client.subscribe(topic, qos, invocationContext, activityToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to one or more topics
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifies the MqttConnection to use
|
||||
* @param topic
|
||||
* a list of possibly wildcarded topic names
|
||||
* @param qos
|
||||
* requested quality of service for each topic
|
||||
* @param invocationContext
|
||||
* arbitrary data to be passed back to the application
|
||||
* @param activityToken
|
||||
* arbitrary identifier to be passed back to the Activity
|
||||
*/
|
||||
public void subscribe(String clientHandle, String[] topic, int[] qos,
|
||||
String invocationContext, String activityToken) {
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
client.subscribe(topic, qos, invocationContext, activityToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe using topic filters
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifies the MqttConnection to use
|
||||
* @param topicFilters
|
||||
* a list of possibly wildcarded topicfilters
|
||||
* @param qos
|
||||
* requested quality of service for each topic
|
||||
* @param invocationContext
|
||||
* arbitrary data to be passed back to the application
|
||||
* @param activityToken
|
||||
* arbitrary identifier to be passed back to the Activity
|
||||
* @param messageListeners a callback to handle incoming messages
|
||||
*/
|
||||
public void subscribe(String clientHandle, String[] topicFilters, int[] qos, String invocationContext, String activityToken, IMqttMessageListener[] messageListeners){
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
client.subscribe(topicFilters, qos, invocationContext, activityToken, messageListeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from a topic
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifies the MqttConnection
|
||||
* @param topic
|
||||
* a possibly wildcarded topic name
|
||||
* @param invocationContext
|
||||
* arbitrary data to be passed back to the application
|
||||
* @param activityToken
|
||||
* arbitrary identifier to be passed back to the Activity
|
||||
*/
|
||||
public void unsubscribe(String clientHandle, final String topic,
|
||||
String invocationContext, String activityToken) {
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
client.unsubscribe(topic, invocationContext, activityToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from one or more topics
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifies the MqttConnection
|
||||
* @param topic
|
||||
* a list of possibly wildcarded topic names
|
||||
* @param invocationContext
|
||||
* arbitrary data to be passed back to the application
|
||||
* @param activityToken
|
||||
* arbitrary identifier to be passed back to the Activity
|
||||
*/
|
||||
public void unsubscribe(String clientHandle, final String[] topic,
|
||||
String invocationContext, String activityToken) {
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
client.unsubscribe(topic, invocationContext, activityToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tokens for all outstanding deliveries for a client
|
||||
*
|
||||
* @param clientHandle
|
||||
* identifies the MqttConnection
|
||||
* @return an array (possibly empty) of tokens
|
||||
*/
|
||||
public IMqttDeliveryToken[] getPendingDeliveryTokens(String clientHandle) {
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
return client.getPendingDeliveryTokens();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the MqttConnection identified by this client handle
|
||||
*
|
||||
* @param clientHandle identifies the MqttConnection
|
||||
* @return the MqttConnection identified by this handle
|
||||
*/
|
||||
private MqttConnection getConnection(String clientHandle) {
|
||||
MqttConnection client = connections.get(clientHandle);
|
||||
if (client == null) {
|
||||
throw new IllegalArgumentException("Invalid ClientHandle");
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the Activity when a message has been passed back to the
|
||||
* application
|
||||
*
|
||||
* @param clientHandle identifier for the client which received the message
|
||||
* @param id identifier for the MQTT message
|
||||
* @return {@link Status}
|
||||
*/
|
||||
public Status acknowledgeMessageArrival(String clientHandle, String id) {
|
||||
if (messageStore.discardArrived(clientHandle, id)) {
|
||||
return Status.OK;
|
||||
}
|
||||
else {
|
||||
return Status.ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// Extend Service
|
||||
|
||||
/**
|
||||
* @see android.app.Service#onCreate()
|
||||
*/
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// create a binder that will let the Activity UI send
|
||||
// commands to the Service
|
||||
mqttServiceBinder = new MqttServiceBinder(this);
|
||||
|
||||
// create somewhere to buffer received messages until
|
||||
// we know that they have been passed to the application
|
||||
messageStore = new DatabaseMessageStore(this, this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see android.app.Service#onDestroy()
|
||||
*/
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
// disconnect immediately
|
||||
for (MqttConnection client : connections.values()) {
|
||||
client.disconnect(null, null);
|
||||
}
|
||||
|
||||
// clear down
|
||||
if (mqttServiceBinder != null) {
|
||||
mqttServiceBinder = null;
|
||||
}
|
||||
|
||||
unregisterBroadcastReceivers();
|
||||
|
||||
if (this.messageStore !=null )
|
||||
this.messageStore.close();
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see android.app.Service#onBind(Intent)
|
||||
*/
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
// What we pass back to the Activity on binding -
|
||||
// a reference to ourself, and the activityToken
|
||||
// we were given when started
|
||||
String activityToken = intent
|
||||
.getStringExtra(MqttServiceConstants.CALLBACK_ACTIVITY_TOKEN);
|
||||
mqttServiceBinder.setActivityToken(activityToken);
|
||||
return mqttServiceBinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see android.app.Service#onStartCommand(Intent,int,int)
|
||||
*/
|
||||
@Override
|
||||
public int onStartCommand(final Intent intent, int flags, final int startId) {
|
||||
// run till explicitly stopped, restart when
|
||||
// process restarted
|
||||
registerBroadcastReceivers();
|
||||
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify the callbackId to be passed when making tracing calls back into
|
||||
* the Activity
|
||||
*
|
||||
* @param traceCallbackId identifier to the callback into the Activity
|
||||
*/
|
||||
public void setTraceCallbackId(String traceCallbackId) {
|
||||
this.traceCallbackId = traceCallbackId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn tracing on and off
|
||||
*
|
||||
* @param traceEnabled set <code>true</code> to turn on tracing, <code>false</code> to turn off tracing
|
||||
*/
|
||||
public void setTraceEnabled(boolean traceEnabled) {
|
||||
this.traceEnabled = traceEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether trace is on or off.
|
||||
*
|
||||
* @return the state of trace
|
||||
*/
|
||||
public boolean isTraceEnabled(){
|
||||
return this.traceEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace debugging information
|
||||
*
|
||||
* @param tag
|
||||
* identifier for the source of the trace
|
||||
* @param message
|
||||
* the text to be traced
|
||||
*/
|
||||
@Override
|
||||
public void traceDebug(String tag, String message) {
|
||||
traceCallback(MqttServiceConstants.TRACE_DEBUG, tag, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace error information
|
||||
*
|
||||
* @param tag
|
||||
* identifier for the source of the trace
|
||||
* @param message
|
||||
* the text to be traced
|
||||
*/
|
||||
@Override
|
||||
public void traceError(String tag, String message) {
|
||||
traceCallback(MqttServiceConstants.TRACE_ERROR, tag, message);
|
||||
}
|
||||
|
||||
private void traceCallback(String severity, String tag, String message) {
|
||||
if ((traceCallbackId != null) && (traceEnabled)) {
|
||||
Bundle dataBundle = new Bundle();
|
||||
dataBundle.putString(MqttServiceConstants.CALLBACK_ACTION, MqttServiceConstants.TRACE_ACTION);
|
||||
dataBundle.putString(MqttServiceConstants.CALLBACK_TRACE_SEVERITY, severity);
|
||||
dataBundle.putString(MqttServiceConstants.CALLBACK_TRACE_TAG, tag);
|
||||
//dataBundle.putString(MqttServiceConstants.CALLBACK_TRACE_ID, traceCallbackId);
|
||||
dataBundle.putString(MqttServiceConstants.CALLBACK_ERROR_MESSAGE, message);
|
||||
callbackToActivity(traceCallbackId, Status.ERROR, dataBundle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* trace exceptions
|
||||
*
|
||||
* @param tag
|
||||
* identifier for the source of the trace
|
||||
* @param message
|
||||
* the text to be traced
|
||||
* @param e
|
||||
* the exception
|
||||
*/
|
||||
@Override
|
||||
public void traceException(String tag, String message, Exception e) {
|
||||
if (traceCallbackId != null) {
|
||||
Bundle dataBundle = new Bundle();
|
||||
dataBundle.putString(MqttServiceConstants.CALLBACK_ACTION, MqttServiceConstants.TRACE_ACTION);
|
||||
dataBundle.putString(MqttServiceConstants.CALLBACK_TRACE_SEVERITY, MqttServiceConstants.TRACE_EXCEPTION);
|
||||
dataBundle.putString(MqttServiceConstants.CALLBACK_ERROR_MESSAGE, message);
|
||||
dataBundle.putSerializable(MqttServiceConstants.CALLBACK_EXCEPTION, e); //TODO: Check
|
||||
dataBundle.putString(MqttServiceConstants.CALLBACK_TRACE_TAG, tag);
|
||||
//dataBundle.putString(MqttServiceConstants.CALLBACK_TRACE_ID, traceCallbackId);
|
||||
callbackToActivity(traceCallbackId, Status.ERROR, dataBundle);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void registerBroadcastReceivers() {
|
||||
if (networkConnectionMonitor == null) {
|
||||
networkConnectionMonitor = new NetworkConnectionIntentReceiver();
|
||||
registerReceiver(networkConnectionMonitor, new IntentFilter(
|
||||
ConnectivityManager.CONNECTIVITY_ACTION));
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < 14 /**Build.VERSION_CODES.ICE_CREAM_SANDWICH**/) {
|
||||
// Support the old system for background data preferences
|
||||
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
|
||||
backgroundDataEnabled = cm.getBackgroundDataSetting();
|
||||
if (backgroundDataPreferenceMonitor == null) {
|
||||
backgroundDataPreferenceMonitor = new BackgroundDataPreferenceReceiver();
|
||||
registerReceiver(
|
||||
backgroundDataPreferenceMonitor,
|
||||
new IntentFilter(
|
||||
ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterBroadcastReceivers(){
|
||||
if(networkConnectionMonitor != null){
|
||||
unregisterReceiver(networkConnectionMonitor);
|
||||
networkConnectionMonitor = null;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < 14 /**Build.VERSION_CODES.ICE_CREAM_SANDWICH**/) {
|
||||
if(backgroundDataPreferenceMonitor != null){
|
||||
unregisterReceiver(backgroundDataPreferenceMonitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called in response to a change in network connection - after losing a
|
||||
* connection to the server, this allows us to wait until we have a usable
|
||||
* data connection again
|
||||
*/
|
||||
private class NetworkConnectionIntentReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
@SuppressLint("Wakelock")
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
traceDebug(TAG, "Internal network status receive.");
|
||||
// we protect against the phone switching off
|
||||
// by requesting a wake lock - we request the minimum possible wake
|
||||
// lock - just enough to keep the CPU running until we've finished
|
||||
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
|
||||
WakeLock wl = pm
|
||||
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MQTT");
|
||||
wl.acquire();
|
||||
traceDebug(TAG,"Reconnect for Network recovery.");
|
||||
if (isOnline()) {
|
||||
traceDebug(TAG,"Online,reconnect.");
|
||||
// we have an internet connection - have another try at
|
||||
// connecting
|
||||
reconnect();
|
||||
} else {
|
||||
notifyClientsOffline();
|
||||
}
|
||||
|
||||
wl.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the android service can be regarded as online
|
||||
*/
|
||||
public boolean isOnline() {
|
||||
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
|
||||
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
|
||||
//noinspection RedundantIfStatement
|
||||
if (networkInfo != null
|
||||
&& networkInfo.isAvailable()
|
||||
&& networkInfo.isConnected()
|
||||
&& backgroundDataEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify clients we're offline
|
||||
*/
|
||||
private void notifyClientsOffline() {
|
||||
for (MqttConnection connection : connections.values()) {
|
||||
connection.offline();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect changes of the Allow Background Data setting - only used below
|
||||
* ICE_CREAM_SANDWICH
|
||||
*/
|
||||
private class BackgroundDataPreferenceReceiver extends BroadcastReceiver {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
|
||||
traceDebug(TAG,"Reconnect since BroadcastReceiver.");
|
||||
if (cm.getBackgroundDataSetting()) {
|
||||
if (!backgroundDataEnabled) {
|
||||
backgroundDataEnabled = true;
|
||||
// we have the Internet connection - have another try at
|
||||
// connecting
|
||||
reconnect();
|
||||
}
|
||||
} else {
|
||||
backgroundDataEnabled = false;
|
||||
notifyClientsOffline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the DisconnectedBufferOptions for this client
|
||||
* @param clientHandle identifier for the client
|
||||
* @param bufferOpts the DisconnectedBufferOptions for this client
|
||||
*/
|
||||
public void setBufferOpts(String clientHandle, DisconnectedBufferOptions bufferOpts) {
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
client.setBufferOpts(bufferOpts);
|
||||
}
|
||||
|
||||
public int getBufferedMessageCount(String clientHandle){
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
return client.getBufferedMessageCount();
|
||||
}
|
||||
|
||||
public MqttMessage getBufferedMessage(String clientHandle, int bufferIndex){
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
return client.getBufferedMessage(bufferIndex);
|
||||
}
|
||||
|
||||
public void deleteBufferedMessage(String clientHandle, int bufferIndex){
|
||||
MqttConnection client = getConnection(clientHandle);
|
||||
client.deleteBufferedMessage(bufferIndex);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 1999, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*/
|
||||
package org.eclipse.paho.android.service;
|
||||
|
||||
import android.os.Binder;
|
||||
|
||||
/**
|
||||
* What the Service passes to the Activity on binding:-
|
||||
* <ul>
|
||||
* <li>a reference to the Service
|
||||
* <li>the activityToken provided when the Service was started
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
class MqttServiceBinder extends Binder {
|
||||
|
||||
private MqttService mqttService;
|
||||
private String activityToken;
|
||||
|
||||
MqttServiceBinder(MqttService mqttService) {
|
||||
this.mqttService = mqttService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a reference to the Service
|
||||
*/
|
||||
public MqttService getService() {
|
||||
return mqttService;
|
||||
}
|
||||
|
||||
void setActivityToken(String activityToken) {
|
||||
this.activityToken = activityToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the activityToken provided when the Service was started
|
||||
*/
|
||||
public String getActivityToken() {
|
||||
return activityToken;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 1999, 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*/
|
||||
package org.eclipse.paho.android.service;
|
||||
|
||||
/**
|
||||
* Various strings used to identify operations or data in the Android MQTT
|
||||
* service, mainly used in Intents passed between Activities and the Service.
|
||||
*/
|
||||
interface MqttServiceConstants {
|
||||
|
||||
/*
|
||||
* Version information
|
||||
*/
|
||||
|
||||
String VERSION = "v0";
|
||||
|
||||
/*
|
||||
* Attributes of messages <p> Used for the column names in the database
|
||||
*/
|
||||
String DUPLICATE = "duplicate";
|
||||
String RETAINED = "retained";
|
||||
String QOS = "qos";
|
||||
String PAYLOAD = "payload";
|
||||
String DESTINATION_NAME = "destinationName";
|
||||
String CLIENT_HANDLE = "clientHandle";
|
||||
String MESSAGE_ID = "messageId";
|
||||
|
||||
/* Tags for actions passed between the Activity and the Service */
|
||||
String SEND_ACTION = "send";
|
||||
String UNSUBSCRIBE_ACTION = "unsubscribe";
|
||||
String SUBSCRIBE_ACTION = "subscribe";
|
||||
String DISCONNECT_ACTION = "disconnect";
|
||||
String CONNECT_ACTION = "connect";
|
||||
String CONNECT_EXTENDED_ACTION = "connectExtended";
|
||||
String MESSAGE_ARRIVED_ACTION = "messageArrived";
|
||||
String MESSAGE_DELIVERED_ACTION = "messageDelivered";
|
||||
String ON_CONNECTION_LOST_ACTION = "onConnectionLost";
|
||||
String TRACE_ACTION = "trace";
|
||||
|
||||
/* Identifies an Intent which calls back to the Activity */
|
||||
String CALLBACK_TO_ACTIVITY = MqttService.TAG
|
||||
+ ".callbackToActivity"+"."+VERSION;
|
||||
|
||||
/* Identifiers for extra data on Intents broadcast to the Activity */
|
||||
String CALLBACK_ACTION = MqttService.TAG + ".callbackAction";
|
||||
String CALLBACK_STATUS = MqttService.TAG + ".callbackStatus";
|
||||
String CALLBACK_CLIENT_HANDLE = MqttService.TAG + "."
|
||||
+ CLIENT_HANDLE;
|
||||
String CALLBACK_ERROR_MESSAGE = MqttService.TAG
|
||||
+ ".errorMessage";
|
||||
String CALLBACK_EXCEPTION_STACK = MqttService.TAG
|
||||
+ ".exceptionStack";
|
||||
String CALLBACK_INVOCATION_CONTEXT = MqttService.TAG + "."
|
||||
+ "invocationContext";
|
||||
String CALLBACK_ACTIVITY_TOKEN = MqttService.TAG + "."
|
||||
+ "activityToken";
|
||||
String CALLBACK_DESTINATION_NAME = MqttService.TAG + '.'
|
||||
+ DESTINATION_NAME;
|
||||
String CALLBACK_MESSAGE_ID = MqttService.TAG + '.'
|
||||
+ MESSAGE_ID;
|
||||
String CALLBACK_RECONNECT = MqttService.TAG + ".reconnect";
|
||||
String CALLBACK_SERVER_URI = MqttService.TAG + ".serverURI";
|
||||
String CALLBACK_MESSAGE_PARCEL = MqttService.TAG + ".PARCEL";
|
||||
String CALLBACK_TRACE_SEVERITY = MqttService.TAG
|
||||
+ ".traceSeverity";
|
||||
String CALLBACK_TRACE_TAG = MqttService.TAG + ".traceTag";
|
||||
String CALLBACK_TRACE_ID = MqttService.TAG + ".traceId";
|
||||
String CALLBACK_ERROR_NUMBER = MqttService.TAG
|
||||
+ ".ERROR_NUMBER";
|
||||
|
||||
String CALLBACK_EXCEPTION = MqttService.TAG + ".exception";
|
||||
|
||||
//Intent prefix for Ping sender.
|
||||
String PING_SENDER = MqttService.TAG + ".pingSender.";
|
||||
|
||||
//Constant for wakelock
|
||||
String PING_WAKELOCK = MqttService.TAG + ".client.";
|
||||
String WAKELOCK_NETWORK_INTENT = MqttService.TAG + "";
|
||||
|
||||
//Trace severity levels
|
||||
String TRACE_ERROR = "error";
|
||||
String TRACE_DEBUG = "debug";
|
||||
String TRACE_EXCEPTION = "exception";
|
||||
|
||||
|
||||
//exception code for non MqttExceptions
|
||||
int NON_MQTT_EXCEPTION = -1;
|
||||
|
||||
}
|
@ -0,0 +1,252 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 1999, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*/
|
||||
package org.eclipse.paho.android.service;
|
||||
|
||||
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
|
||||
import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
|
||||
import org.eclipse.paho.client.mqttv3.IMqttToken;
|
||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||
import org.eclipse.paho.client.mqttv3.MqttSecurityException;
|
||||
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Implementation of the IMqttToken interface for use from within the
|
||||
* MqttAndroidClient implementation
|
||||
*/
|
||||
|
||||
class MqttTokenAndroid implements IMqttToken {
|
||||
|
||||
private IMqttActionListener listener;
|
||||
|
||||
private volatile boolean isComplete;
|
||||
|
||||
private volatile MqttException lastException;
|
||||
|
||||
private Object waitObject = new Object();
|
||||
|
||||
private MqttAndroidClient client;
|
||||
|
||||
private Object userContext;
|
||||
|
||||
private String[] topics;
|
||||
|
||||
private IMqttToken delegate; // specifically for getMessageId
|
||||
|
||||
private MqttException pendingException;
|
||||
|
||||
/**
|
||||
* Standard constructor
|
||||
*
|
||||
* @param client used to pass MqttAndroidClient object
|
||||
* @param userContext used to pass context
|
||||
* @param listener optional listener that will be notified when the action completes. Use null if not required.
|
||||
*/
|
||||
MqttTokenAndroid(MqttAndroidClient client,
|
||||
Object userContext, IMqttActionListener listener) {
|
||||
this(client, userContext, listener, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for use with subscribe operations
|
||||
*
|
||||
* @param client used to pass MqttAndroidClient object
|
||||
* @param userContext used to pass context
|
||||
* @param listener optional listener that will be notified when the action completes. Use null if not required.
|
||||
* @param topics topics to subscribe to, which can include wildcards.
|
||||
*/
|
||||
MqttTokenAndroid(MqttAndroidClient client,
|
||||
Object userContext, IMqttActionListener listener, String[] topics) {
|
||||
this.client = client;
|
||||
this.userContext = userContext;
|
||||
this.listener = listener;
|
||||
this.topics = topics;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.paho.client.mqttv3.IMqttToken#waitForCompletion()
|
||||
*/
|
||||
@Override
|
||||
public void waitForCompletion() throws MqttException, MqttSecurityException {
|
||||
synchronized (waitObject) {
|
||||
try {
|
||||
waitObject.wait();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
if (pendingException != null) {
|
||||
throw pendingException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.paho.client.mqttv3.IMqttToken#waitForCompletion(long)
|
||||
*/
|
||||
@Override
|
||||
public void waitForCompletion(long timeout) throws MqttException,
|
||||
MqttSecurityException {
|
||||
synchronized (waitObject) {
|
||||
try {
|
||||
waitObject.wait(timeout);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// do nothing
|
||||
}
|
||||
if (!isComplete) {
|
||||
throw new MqttException(MqttException.REASON_CODE_CLIENT_TIMEOUT);
|
||||
}
|
||||
if (pendingException != null) {
|
||||
throw pendingException;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* notify successful completion of the operation
|
||||
*/
|
||||
void notifyComplete() {
|
||||
synchronized (waitObject) {
|
||||
isComplete = true;
|
||||
waitObject.notifyAll();
|
||||
if (listener != null) {
|
||||
listener.onSuccess(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* notify unsuccessful completion of the operation
|
||||
*/
|
||||
void notifyFailure(Throwable exception) {
|
||||
synchronized (waitObject) {
|
||||
isComplete = true;
|
||||
if (exception instanceof MqttException) {
|
||||
pendingException = (MqttException) exception;
|
||||
}
|
||||
else {
|
||||
pendingException = new MqttException(exception);
|
||||
}
|
||||
waitObject.notifyAll();
|
||||
if (exception instanceof MqttException) {
|
||||
lastException = (MqttException) exception;
|
||||
}
|
||||
if (listener != null) {
|
||||
listener.onFailure(this, exception);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.paho.client.mqttv3.IMqttToken#isComplete()
|
||||
*/
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
return isComplete;
|
||||
}
|
||||
|
||||
void setComplete(boolean complete) {
|
||||
isComplete = complete;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.paho.client.mqttv3.IMqttToken#getException()
|
||||
*/
|
||||
@Override
|
||||
public MqttException getException() {
|
||||
return lastException;
|
||||
}
|
||||
|
||||
void setException(MqttException exception) {
|
||||
lastException = exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.paho.client.mqttv3.IMqttToken#getClient()
|
||||
*/
|
||||
@Override
|
||||
public IMqttAsyncClient getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.paho.client.mqttv3.IMqttToken#setActionCallback(IMqttActionListener)
|
||||
*/
|
||||
@Override
|
||||
public void setActionCallback(IMqttActionListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.paho.client.mqttv3.IMqttToken#getActionCallback()
|
||||
*/
|
||||
@Override
|
||||
public IMqttActionListener getActionCallback() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.paho.client.mqttv3.IMqttToken#getTopics()
|
||||
*/
|
||||
@Override
|
||||
public String[] getTopics() {
|
||||
return topics;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.paho.client.mqttv3.IMqttToken#setUserContext(Object)
|
||||
*/
|
||||
@Override
|
||||
public void setUserContext(Object userContext) {
|
||||
this.userContext = userContext;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.paho.client.mqttv3.IMqttToken#getUserContext()
|
||||
*/
|
||||
@Override
|
||||
public Object getUserContext() {
|
||||
return userContext;
|
||||
}
|
||||
|
||||
void setDelegate(IMqttToken delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.paho.client.mqttv3.IMqttToken#getMessageId()
|
||||
*/
|
||||
@Override
|
||||
public int getMessageId() {
|
||||
return (delegate != null) ? delegate.getMessageId() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttWireMessage getResponse() {
|
||||
return delegate.getResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getSessionPresent() {
|
||||
return delegate.getSessionPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getGrantedQos() {
|
||||
return delegate.getGrantedQos();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 1999, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*/
|
||||
package org.eclipse.paho.android.service;
|
||||
|
||||
/**
|
||||
* Interface for simple trace handling, pass the trace message to trace
|
||||
* callback.
|
||||
*
|
||||
*/
|
||||
|
||||
public interface MqttTraceHandler {
|
||||
|
||||
/**
|
||||
* Trace debugging information
|
||||
*
|
||||
* @param tag
|
||||
* identifier for the source of the trace
|
||||
* @param message
|
||||
* the text to be traced
|
||||
*/
|
||||
void traceDebug(String tag, String message);
|
||||
|
||||
/**
|
||||
* Trace error information
|
||||
*
|
||||
* @param tag
|
||||
* identifier for the source of the trace
|
||||
* @param message
|
||||
* the text to be traced
|
||||
*/
|
||||
void traceError(String tag, String message);
|
||||
|
||||
/**
|
||||
* trace exceptions
|
||||
*
|
||||
* @param tag
|
||||
* identifier for the source of the trace
|
||||
* @param message
|
||||
* the text to be traced
|
||||
* @param e
|
||||
* the exception
|
||||
*/
|
||||
void traceException(String tag, String message,
|
||||
Exception e);
|
||||
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 1999, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*/
|
||||
package org.eclipse.paho.android.service;
|
||||
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A way to flow MqttMessages via Bundles/Intents
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* An application will probably use this only when receiving a message from a
|
||||
* Service in a Bundle - the necessary code will be something like this :-
|
||||
* </p>
|
||||
* <pre>
|
||||
* <code>
|
||||
* private void messageArrivedAction(Bundle data) {
|
||||
* ParcelableMqttMessage message = (ParcelableMqttMessage) data
|
||||
* .getParcelable(MqttServiceConstants.CALLBACK_MESSAGE_PARCEL);
|
||||
* <i>Use the normal {@link MqttMessage} methods on the the message object.</i>
|
||||
* }
|
||||
*
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* It is unlikely that an application will directly use the methods which are
|
||||
* specific to this class.
|
||||
* </p>
|
||||
*/
|
||||
|
||||
public class ParcelableMqttMessage extends MqttMessage implements Parcelable {
|
||||
|
||||
String messageId = null;
|
||||
|
||||
ParcelableMqttMessage(MqttMessage original) {
|
||||
super(original.getPayload());
|
||||
setQos(original.getQos());
|
||||
setRetained(original.isRetained());
|
||||
setDuplicate(original.isDuplicate());
|
||||
}
|
||||
|
||||
ParcelableMqttMessage(Parcel parcel) {
|
||||
super(parcel.createByteArray());
|
||||
setQos(parcel.readInt());
|
||||
boolean[] flags = parcel.createBooleanArray();
|
||||
setRetained(flags[0]);
|
||||
setDuplicate(flags[1]);
|
||||
messageId = parcel.readString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the messageId
|
||||
*/
|
||||
public String getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the contents of this object
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the contents of this object to a parcel
|
||||
*
|
||||
* @param parcel
|
||||
* The parcel to write the data to.
|
||||
* @param flags
|
||||
* this parameter is ignored
|
||||
*/
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
parcel.writeByteArray(getPayload());
|
||||
parcel.writeInt(getQos());
|
||||
parcel.writeBooleanArray(new boolean[]{isRetained(), isDuplicate()});
|
||||
parcel.writeString(messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* A creator which creates the message object from a parcel
|
||||
*/
|
||||
public static final Parcelable.Creator<ParcelableMqttMessage> CREATOR = new Parcelable.Creator<ParcelableMqttMessage>() {
|
||||
|
||||
/**
|
||||
* Creates a message from the parcel object
|
||||
*/
|
||||
@Override
|
||||
public ParcelableMqttMessage createFromParcel(Parcel parcel) {
|
||||
return new ParcelableMqttMessage(parcel);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates an array of type {@link ParcelableMqttMessage}[]
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public ParcelableMqttMessage[] newArray(int size) {
|
||||
return new ParcelableMqttMessage[size];
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package org.eclipse.paho.android.service;
|
||||
|
||||
import org.eclipse.paho.client.mqttv3.MqttPingSender;
|
||||
import org.eclipse.paho.client.mqttv3.internal.ClientComms;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class RitoPingSender implements MqttPingSender {
|
||||
|
||||
private ClientComms comms;
|
||||
private ScheduledExecutorService scheduler;
|
||||
private ScheduledFuture<?> pingFuture;
|
||||
|
||||
@Override
|
||||
public void init(ClientComms comms) {
|
||||
this.comms = comms;
|
||||
this.scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
schedule(comms.getKeepAlive());
|
||||
}
|
||||
|
||||
private void schedule(int delayInSeconds) {
|
||||
pingFuture = scheduler.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
comms.checkForActivity();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, delayInSeconds, delayInSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (pingFuture != null) pingFuture.cancel(true);
|
||||
scheduler.shutdownNow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void schedule(long delayInMilliseconds) {
|
||||
if (pingFuture != null) pingFuture.cancel(true);
|
||||
pingFuture = scheduler.schedule(() -> {
|
||||
try {
|
||||
comms.checkForActivity();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, delayInMilliseconds, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 1999, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*/
|
||||
package org.eclipse.paho.android.service;
|
||||
|
||||
/**
|
||||
* Enumeration representing the success or failure of an operation
|
||||
*/
|
||||
enum Status {
|
||||
/**
|
||||
* Indicates that the operation succeeded
|
||||
*/
|
||||
OK,
|
||||
|
||||
/**
|
||||
* Indicates that the operation failed
|
||||
*/
|
||||
ERROR,
|
||||
|
||||
/**
|
||||
* Indicates that the operation's result may be returned asynchronously
|
||||
*/
|
||||
NO_RESULT
|
||||
}
|
30
app/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
BIN
app/src/main/res/drawable/blackground.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
app/src/main/res/drawable/channel_osd.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
app/src/main/res/drawable/demo_119.png
Normal file
After Width: | Height: | Size: 1.3 MiB |
170
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
BIN
app/src/main/res/drawable/no_broadcast.png
Normal file
After Width: | Height: | Size: 124 KiB |
BIN
app/src/main/res/drawable/on_tts.png
Normal file
After Width: | Height: | Size: 46 KiB |
26
app/src/main/res/drawable/selector_button.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- 버튼이 눌렸을 때 -->
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FF018786" />
|
||||
<stroke android:width="4dp" android:color="#FF6200EE" />
|
||||
</shape>
|
||||
</item> <!-- 버튼이 안 눌렸을 때(디폴트) -->
|
||||
|
||||
<item android:state_focused="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FFA3AAA5" />
|
||||
<stroke android:width="4dp" android:color="#FFAA0022" />
|
||||
</shape>
|
||||
</item> <!-- FOCUSSED -->
|
||||
|
||||
<item android:state_pressed="false">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FFA3AAA5" />
|
||||
<stroke android:width="2dp" android:color="#000000" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</selector>
|
BIN
app/src/main/res/drawable/splash.png
Normal file
After Width: | Height: | Size: 276 KiB |
BIN
app/src/main/res/drawable/wait_broadcast.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
app/src/main/res/drawable/webview_loading.png
Normal file
After Width: | Height: | Size: 20 KiB |
203
app/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,203 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_x="300px"
|
||||
android:layout_y="200px"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#000000"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<WebView
|
||||
android:id="@+id/webview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="invisible">
|
||||
|
||||
</WebView>
|
||||
|
||||
<TextureView
|
||||
android:id="@+id/texturePlayView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewLoading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:alpha="0"
|
||||
android:contentDescription="@string/default_desc"
|
||||
android:visibility="visible"
|
||||
app:srcCompat="@drawable/webview_loading" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewCarcast"
|
||||
android:layout_width="510px"
|
||||
android:layout_height="70px"
|
||||
android:layout_marginStart="0px"
|
||||
android:layout_marginTop="0px"
|
||||
android:background="#80F6F1F1"
|
||||
android:gravity="center_vertical"
|
||||
android:text="재난방송명"
|
||||
android:textColor="#050505"
|
||||
android:textSize="35px"
|
||||
android:textStyle="bold"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewWait"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/default_desc"
|
||||
android:visibility="visible"
|
||||
app:srcCompat="@drawable/wait_broadcast" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewDemo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/default_desc"
|
||||
android:visibility="invisible"
|
||||
app:srcCompat="@drawable/demo_119" />
|
||||
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewTts"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:alpha="0"
|
||||
android:contentDescription="@string/default_desc"
|
||||
android:visibility="visible"
|
||||
app:srcCompat="@drawable/on_tts" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewBlack"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="invisible"
|
||||
app:srcCompat="@drawable/blackground" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewChannelOsd"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="90px"
|
||||
android:layout_marginLeft="80px"
|
||||
android:layout_marginTop="50px"
|
||||
android:visibility="invisible"
|
||||
app:srcCompat="@drawable/channel_osd" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="920px"
|
||||
android:layout_marginTop="500px"
|
||||
android:scaleX="2"
|
||||
android:scaleY="2"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<SurfaceView
|
||||
android:id="@+id/playerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewChannelOsd"
|
||||
android:layout_width="310px"
|
||||
android:layout_height="90px"
|
||||
android:layout_marginLeft="170px"
|
||||
android:layout_marginTop="50px"
|
||||
android:gravity="center"
|
||||
android:text="채널명"
|
||||
android:textAlignment="center"
|
||||
android:textColor="#050505"
|
||||
android:textSize="35px"
|
||||
android:textStyle="bold"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewTicker"
|
||||
android:layout_width="1920px"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="0px"
|
||||
android:alpha="0"
|
||||
android:background="#80000000"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:gravity="center"
|
||||
android:marqueeRepeatLimit="marquee_forever"
|
||||
android:scrollHorizontally="true"
|
||||
android:singleLine="true"
|
||||
android:textColor="#ff4500"
|
||||
android:textSize="140px" />
|
||||
|
||||
<!--<kr.co.rito.osicmanager.TickerView
|
||||
android:id="@+id/tickerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />-->
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layoutCommand"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewCmdBack"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
app:srcCompat="@drawable/blackground" />
|
||||
|
||||
<SurfaceView
|
||||
android:id="@+id/surfaceViewCmd"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layoutAlert"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewAlert"
|
||||
android:layout_width="700px"
|
||||
android:layout_height="400px"
|
||||
android:layout_marginLeft="600px"
|
||||
android:layout_marginTop="300px"
|
||||
android:background="#CC281F4E"
|
||||
android:gravity="center"
|
||||
android:text="TextView"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="34sp"
|
||||
android:visibility="visible" />
|
||||
</FrameLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
51
app/src/main/res/layout/activity_setting.xml
Normal file
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".SettingActivity">
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/view_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:minHeight="?actionBarSize"
|
||||
android:padding="@dimen/appbar_padding"
|
||||
android:text="설 정"
|
||||
android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="64px" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:tabGravity="fill"
|
||||
app:tabMaxWidth="0dp"
|
||||
app:tabMode="fixed"
|
||||
app:tabTextAppearance="@style/MineCustomTabText" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/fab_margin"
|
||||
android:visibility="invisible"
|
||||
app:srcCompat="@android:drawable/ic_dialog_email" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
399
app/src/main/res/layout/fragment_setting.xml
Normal file
@ -0,0 +1,399 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/constraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.main.PlaceholderFragment">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layoutServer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="800px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="100px"
|
||||
android:layout_marginTop="20px"
|
||||
android:gravity="left|center_vertical"
|
||||
android:text="SERVER SETTING"
|
||||
android:textColor="#000000"
|
||||
android:textSize="90px" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextServerIpValue"
|
||||
android:layout_width="1200px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="440px"
|
||||
android:layout_marginTop="150px"
|
||||
android:background="#CDDC39"
|
||||
android:digits="0123456789."
|
||||
android:gravity="left|center_vertical"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="number"
|
||||
android:paddingStart="30px"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextServerPortValue"
|
||||
android:layout_width="1200px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="440px"
|
||||
android:layout_marginTop="260px"
|
||||
android:background="#CDDC39"
|
||||
android:digits="0123456789."
|
||||
android:gravity="left|center_vertical"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="number"
|
||||
android:paddingStart="30px"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewServerIp"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginTop="150px"
|
||||
android:gravity="right|center_vertical"
|
||||
android:text="IP"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewServerPort"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginTop="260px"
|
||||
android:gravity="right|center_vertical"
|
||||
android:text="Port"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="800px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="100px"
|
||||
android:layout_marginTop="450px"
|
||||
android:gravity="left|center_vertical"
|
||||
android:text="UPDATE SETTING"
|
||||
android:textColor="#000000"
|
||||
android:textSize="90px" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewUpdateUrl"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginTop="590px"
|
||||
android:gravity="right|center_vertical"
|
||||
android:text="ADDRESS"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextUpdateUrlValue"
|
||||
android:layout_width="1200px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="440px"
|
||||
android:layout_marginTop="590px"
|
||||
android:background="#CDDC39"
|
||||
android:gravity="left|center_vertical"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="textUri"
|
||||
android:paddingStart="30px"
|
||||
android:textColor="#000000"
|
||||
android:textSize="60px" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonApplyServer"
|
||||
android:layout_width="300px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="1340px"
|
||||
android:layout_marginTop="750px"
|
||||
android:text="Apply"
|
||||
android:textColor="#000000"
|
||||
android:textSize="50px" />
|
||||
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layoutNetwork"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextNetDnsValue"
|
||||
android:layout_width="1200px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="440px"
|
||||
android:layout_marginTop="490px"
|
||||
android:background="#CDDC39"
|
||||
android:digits="0123456789."
|
||||
android:gravity="left|center_vertical"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="number"
|
||||
android:paddingStart="30px"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextNetGatewayValue"
|
||||
android:layout_width="1200px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="440px"
|
||||
android:layout_marginTop="380px"
|
||||
android:background="#CDDC39"
|
||||
android:digits="0123456789."
|
||||
android:gravity="left|center_vertical"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="number"
|
||||
android:paddingStart="30px"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextNetmaskValue"
|
||||
android:layout_width="1200px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="440px"
|
||||
android:layout_marginTop="270px"
|
||||
android:background="#CDDC39"
|
||||
android:digits="0123456789."
|
||||
android:gravity="left|center_vertical"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="number"
|
||||
android:paddingStart="30px"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextNetIpValue"
|
||||
android:layout_width="1200px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="440px"
|
||||
android:layout_marginTop="160px"
|
||||
android:background="#CDDC39"
|
||||
android:digits="0123456789."
|
||||
android:gravity="left|center_vertical"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="number"
|
||||
android:paddingStart="30px"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<RadioGroup
|
||||
android:layout_width="1200px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="440px"
|
||||
android:layout_marginTop="50px"
|
||||
android:background="#CDDC39"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radioButtonStatic"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="STATIC"
|
||||
android:textSize="64px" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radioButtonDhcp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="DHCP"
|
||||
android:textSize="64px" />
|
||||
</RadioGroup>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewNetDns"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginTop="490px"
|
||||
android:gravity="right|center_vertical"
|
||||
android:text="DNS"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewNetGateway"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginTop="380px"
|
||||
android:gravity="right|center_vertical"
|
||||
android:text="Gateway"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewNetmask"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginTop="270px"
|
||||
android:gravity="right|center_vertical"
|
||||
android:text="Netmask"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewNetIp"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginTop="160px"
|
||||
android:gravity="right|center_vertical"
|
||||
android:text="IP"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewNetIpType"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginTop="50px"
|
||||
android:gravity="right|center_vertical"
|
||||
android:text="IPType"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonApplyNetwork"
|
||||
android:layout_width="300px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="1340px"
|
||||
android:layout_marginTop="600px"
|
||||
android:text="Apply"
|
||||
android:textColor="#000000"
|
||||
android:textSize="50px"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layoutInfo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewInfoMac"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginTop="270px"
|
||||
android:gravity="right|center_vertical"
|
||||
android:text="Mac"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewInfoMacValue"
|
||||
android:layout_width="1200px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="440px"
|
||||
android:layout_marginTop="270px"
|
||||
android:background="#CDDC39"
|
||||
android:gravity="left|center_vertical"
|
||||
android:paddingStart="30px"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewInfoSerial"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginTop="160px"
|
||||
android:gravity="right|center_vertical"
|
||||
android:text="Serial"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewInfoSerialValue"
|
||||
android:layout_width="1200px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="440px"
|
||||
android:layout_marginTop="160px"
|
||||
android:background="#CDDC39"
|
||||
android:gravity="left|center_vertical"
|
||||
android:paddingStart="30px"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewInfoIp"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginTop="380px"
|
||||
android:gravity="right|center_vertical"
|
||||
android:text="IP"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewInfoIpValue"
|
||||
android:layout_width="1200px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="440px"
|
||||
android:layout_marginTop="380px"
|
||||
android:background="#CDDC39"
|
||||
android:gravity="left|center_vertical"
|
||||
android:paddingStart="30px"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewVersion"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginTop="50px"
|
||||
android:gravity="right|center_vertical"
|
||||
android:text="Version"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewVersionValue"
|
||||
android:layout_width="1200px"
|
||||
android:layout_height="100px"
|
||||
android:layout_marginLeft="440px"
|
||||
android:layout_marginTop="50px"
|
||||
android:background="#CDDC39"
|
||||
android:gravity="left|center_vertical"
|
||||
android:paddingStart="30px"
|
||||
android:textColor="#000000"
|
||||
android:textSize="70px" />
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/section_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/activity_vertical_margin"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/constraintLayout"
|
||||
tools:layout_constraintLeft_creator="1"
|
||||
tools:layout_constraintTop_creator="1" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
app/src/main/res/raw/downchimes.wav
Normal file
BIN
app/src/main/res/raw/mute.wav
Normal file
BIN
app/src/main/res/raw/upchimes.wav
Normal file
6
app/src/main/res/values-w820dp/dimens.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||
(such as screen margins) for screens with more than 820dp of available width. This
|
||||
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||
</resources>
|
6
app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#6200EE</color>
|
||||
<color name="colorPrimaryDark">#3700B3</color>
|
||||
<color name="colorAccent">#03DAC5</color>
|
||||
</resources>
|
8
app/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
<dimen name="appbar_padding">16dp</dimen>
|
||||
<dimen name="fab_margin">16dp</dimen>
|
||||
<dimen name="appbar_padding_top">8dp</dimen>
|
||||
</resources>
|
8
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<resources>
|
||||
<string name="app_name">OsicManager</string>
|
||||
<string name="default_desc">Image Desc</string>
|
||||
<string name="title_activity_setting">SettingActivity</string>
|
||||
<string name="tab_text_1">기기 정보</string>
|
||||
<string name="tab_text_2">네트워크 설정</string>
|
||||
<string name="tab_text_3">서버 설정</string>
|
||||
</resources>
|
34
app/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" >
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" >
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="MineCustomTabText" parent="TextAppearance.Design.Tab">
|
||||
<item name="android:textSize">48px</item>
|
||||
<item name="android:layout_marginHorizontal">100px</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
@ -0,0 +1,17 @@
|
||||
package kr.co.rito.osicmanager;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
53
build.gradle
Normal file
@ -0,0 +1,53 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
||||
}
|
||||
dependencies {
|
||||
//classpath 'com.android.tools.build:gradle:4.0.1'
|
||||
classpath "com.android.tools.build:gradle:7.0.2"
|
||||
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
// gradle.projectsEvaluated {
|
||||
// tasks.withType(JavaCompile) {
|
||||
// options.compilerArgs.add('-Xbootclasspath/p:app/libs/ritocls.jar')
|
||||
// }
|
||||
// }
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs.add('-Xbootclasspath/p:app\\libs\\ritocls.jar')
|
||||
}
|
||||
|
||||
// After Android 4.2.x
|
||||
tasks.withType(JavaCompile) {
|
||||
Set<File> fileSet = options.bootstrapClasspath.getFiles()
|
||||
List<File> newFileList = new ArrayList<>();
|
||||
newFileList.add(new File("./app/libs/ritocls.jar"))
|
||||
newFileList.addAll(fileSet)
|
||||
options.bootstrapClasspath = files(
|
||||
newFileList.toArray()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
20
gradle.properties
Normal file
@ -0,0 +1,20 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2536m
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
|
2
settings.gradle
Normal file
@ -0,0 +1,2 @@
|
||||
rootProject.name='OsicManager'
|
||||
include ':app'
|