가. 아래와 같이 xml 파일 생성
프로젝트 > res > xml > mcore_provider_paths.xml
나. mcore_provider_paths.xml 에 아래 내용 추가
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
<root-path
name="root"
path="." />
</paths>
다. AndroidManifest.xml 에 아래와 같이 적용
[AndroidManifest.xml] 파일에 적용
<application>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/mcore_provider_paths"/>
</provider>
</application>
[AndroidManifest.xml] 파일에 적용
<application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/mcore_provider_paths"/>
</provider>
</application>
android.useAndroidX=true
android.enableJetifier=true
implementation fileTree(dir: 'mcoreLibs', include: ['*.jar', '*.aar'], excludes: ['android-support-v4.jar'])
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
Note
${applicationId} 는 package name 을 의미하며, 빌드 오류시 프로젝트에서 사용하는 packageName 을 적용하면 된다.
예: android:authorities="com.morpheus.mobile.provider"
가. app module 의 gradle 파일 (build.gradle)
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'morpheus_proguard-project.txt'
}
}
나. 프로젝트 > morpheus_proguard-project.txt 생성
# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html
# Optimization is turned off by default. Dex does not like code run
# through the ProGuard optimize and preverify steps (and performs some
# of these optimizations on its own).
-dontoptimize
-dontpreverify
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
#To repackage classes on a single package
#-repackageclasses ''
#Uncomment if using annotations to keep them / generic type.
-keepattributes *Annotation*, EnclosingMethod, Signature
#안드로이드 support / apache / json 난독화를 진행하지 않는다.(모든 내용)
-keep class android.support.v4.** { *; }
-keep class com.google.** { *; }
-keep class org.apache.** { *; }
-keep class org.json.** { *; }
-keep class com.nhn.**{ *; }
#kakao 예외 처리
-keep class com.kakao.** { *; }
-keepattributes Signature
-keepclassmembers class * {
public static <fields>;
public *;
}
-dontwarn android.support.v4.**,org.slf4j.**,com.google.android.gms.**
#상기 선언된 내용에 대한 진행 중 발생하는 문제에 대해 경고하지 않는다.
-dontwarn android.support.v4.**
-dontwarn com.google.**
-dontwarn org.apache.**
-dontwarn m.client.android.library.core.bridge.**
-dontwarn m.client.android.library.core.managers.**
-dontwarn pub.devrel.easypermissions.**
-dontwarn m.client.android.library.core.control.**
-dontwarn m.client.android.library.core.utils.**
-dontwarn m.client.android.library.core.view.**
####No obfuscation
#-dontobfuscate
#morpheus push
-dontwarn m.client.push.library.**
-dontwarn mpush.eclipse.paho.**
-dontwarn mpush.eclipse.paho.client.mqttv3.internal.ssl.SecureSocketSslContextFactory
#android
-dontnote android.net.http.*
-dontnote org.apache.http.**
-dontnote org.json.**
-dontnote com.google.**
#Keep classes that are referenced on the AndroidManifest
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.support.v4.app.Fragment
#라이센스 관련 정보가 들어가 있을때는 아래의 주석을 제거한다.
#-keep public class com.google.vending.licensing.ILicensingService
#-keep public class com.android.vending.licensing.ILicensingService
#To maintain custom components names that are used on layouts XML.
#Uncomment if having any problem with the approach below
#-keep public class custom.components.package.and.name.**
# keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
#To remove debug logs:
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
public static *** w(...);
}
#To avoid changing names of methods invoked on layout's onClick.
# Uncomment and add specific method names if using onClick on layouts
#-keepclassmembers class * {
# public void onClickButton(android.view.View);
#}
#Maintain java native methods
-keepclasseswithmembernames class * {
native <methods>;
}
#keep android class using context
-keepclassmembers class * extends android.content.Context {
public void *(android.view.View);
public void *(android.view.MenuItem);
}
#To maintain custom components names that are used on layouts XML:
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
#Maintain enums
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#To keep parcelable classes (to serialize - deserialize objects to sent through Intents)
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#Keep the R
-keepclassmembers class **.R$* {
public static <fields>;
}
###### ADDITIONAL OPTIONS NOT USED NORMALLY
#To keep callback calls. Uncomment if using any
#http://proguard.sourceforge.net/index.html#/manual/examples.html#callback
#-keep class mypackage.MyCallbackClass {
# void myCallbackMethod(java.lang.String);
#}
#Uncomment if using Serializable
-keepclassmembers class * implements java.io.Serializable {
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
#dynamic class loading에 관련된 클래스 들은 난독화를 진행하지 않는다.
-keep public class m.client.android.library.core.managers.WNInterfaceManager
-keep public class m.client.android.library.core.utils.ClassManager
-keep public class m.client.android.library.core.bridge.InterfaceJavascript
-keep public class m.client.android.library.core.common.MorpheusApplication
#callback 함수들을 사용하는 클래스들은 난독화를 진행하지 않는다.
-keep public class * extends m.client.android.library.core.networks.http.AsyncHttpNetwork {
public protected *;
}
-keep public class * extends m.client.android.library.core.networks.socket.AsyncSocketNetwork {
public protected *;
}
#native <-> script interface 함수들은 난독화 하지 않는다.
-keep public class * extends m.client.android.library.core.control.AbstractInterface {
public protected *;
}
-keep class android.support.multidex.MultiDexApplication {
<init>();
void attachBaseContext(android.content.Context);
}
주의 압축 해제 후, 사용
가. morpheus_proguard-project.txt 에 아래 코드를 추가합니다.
-keep class com.artifex.mupdfdemo.** {*;}
가. morpheus_proguard-project.txt 에 아래 코드를 추가합니다.
-keep class net.sourceforge.zbar.** { *; }
가. app module 의 gradle 파일 (build.gradle)
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'mpush_proguard.txt'
}
}
나. 프로젝트 > mpush_proguard.txt 생성
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
#상기 선언된 내용에 대한 진행 중 발생하는 문제에 대해 경고하지 않는다.
-dontwarn android.support.v4.**
-dontwarn com.google.**
-dontwarn org.apache.**
-dontwarn m.client.push.library.**
-dontwarn mpush.eclipse.paho.**
-dontwarn mpush.eclipse.paho.client.mqttv3.internal.ssl.SecureSocketSslContextFactory
-dontwarn mpush.eclipse.paho.client.mqttv3.internal.ssl.SecureSocketSslContextFactory
#Keep classes that are referenced on the AndroidManifest
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.support.v4.app.Fragment
주의 압축 해제 후, 사용
가. AndroidManifest.xml 에 아래 속성 추가
<application>
<uses-library android:name="org.apache.http.legacy" android:required="false" />
</application>
나. build.gradle 에 useLibrary 추가
android {
...
useLibrary 'org.apache.http.legacy'
}
가. 하위 호환성을 위해, 아래와 같이 처리한다. (targetsdk Android 11까지만 지원됨)
<manifest ... >
<!-- This attribute is "false" by default on apps targeting
Android 10 or higher. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
build.gradle 수정
가. android defaultConfig 에 multiDexEnabled 를 true 로 선언
android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 29
multiDexEnabled true
}
}
나. dependencies 적용
// androidx 기준 적용 시
dependencies {
def multidex_version = "2.0.1"
implementation 'androidx.multidex:multidex:$multidex_version'
}
// 이전 버전
dependencies {
implementation 'com.android.support:multidex:1.0.3'
}
ExtendApplication 수정
가. attachBaseContext method 추가
public class ExtendApplication extends MorpheusApplication {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
Android의 경우, HTTPS 프로토콜만 사용하도록 되어 있으나, 라이브러리의 기능 중, HTTP 를 통한 일반 텍스트를 지원하는 기능을 사용하는 경우에 발생합니다.
가. 프로젝트 > res > xml > network_security_config.xml 파일 생성
나. 아래 코드를 생성된 파일에 적용
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">127.0.0.1</domain>
</domain-config>
</network-security-config>
다. AndroidManifest.xml 의 application 에 networkSecurityConfig 속성 추가
<application
android:networkSecurityConfig="@xml/network_security_config"
~~~ >
</application>
Note
android 9 이상은, 보안상의 이유로 HTTP 통신을 차단합니다. 따라서 예외 처리를 하고자 하는 domain 은 위 포멧에서 <domain> 을 추가로 등록하여 사용합니다.
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">127.0.0.1</domain> </domain-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">추가할 domain 정보 </domain> </domain-config> </network-security-config>
예시 :
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">127.0.0.1</domain> </domain-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">morpheus.co.kr</domain> </domain-config> </network-security-config>
주의 사항: (Google Play) 개인정보 관련 명시적 공개 가이드라인을 충족하지 않고 사용자의 전화번호 정보를 수집할 경우 관련 조치를 취하지 않으면 앱이 Reject 될 수 있음. Play Store PhoneNumber 사용 조치
var result = M.execute(“getPhoneNumber”);
alert("전화번호 : "+JSON.stringify(result));
(참고) Android 14 설정 시 : Build version 별로 + 듀얼심을 여부를 구분하여 SubscriptionManager 부분을 제거 하고 사용
public String getPhoneNumber() {
JSONObject object = new JSONObject();
String[] readPhoneNumber_Permissions = {Manifest.permission.READ_PHONE_NUMBERS};
callerObject.checkPermissionsFromInterface(readPhoneNumber_Permissions, PermissionUtil.REQUEST_PERMISSION_STATE, new IRequestPermissionsListener() {
@SuppressLint("MissingPermission")
@Override
public void permissionGranted() {
try {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {//sdk ver 23~
//듀얼심 사용 처리
SubscriptionManager localSubscriptionManager = SubscriptionManager.from(callerObject);
if (localSubscriptionManager.getActiveSubscriptionInfoCount() > 1) {
//if there are two sims in dual sim mobile
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //sdk ver 23~
List<SubscriptionInfo> subscription = SubscriptionManager.from(callerObject).getActiveSubscriptionInfoList();
for (int i = 0; i < subscription.size(); i++) {
SubscriptionInfo info = subscription.get(i);
object.put("sim" + i, info.getNumber());
Log.e("getNumber1::",info.getNumber());
}
}
} else {
//if there is 1 sim in dual sim mobile
TelephonyManager tManager = (TelephonyManager) callerObject.getSystemService(Context.TELEPHONY_SERVICE);
String sim1 = tManager.getLine1Number();
object.put("sim1", sim1);
Log.e("getNumber2::",sim1);
}
} else {
//below android version 22
TelephonyManager tManager = (TelephonyManager) callerObject.getBaseContext()
.getSystemService(Context.TELEPHONY_SERVICE);
String sim1 = tManager.getLine1Number();
object.put("sim1", sim1);
Log.e("getNumber3::",sim1);
}
}catch (JSONException e){
}
Logger.e("permission granted");
}
@Override
public void permissionDenied() {
try {
object.put("status", IOUtils.STR_FAIL);
object.put("error", "Permission Denied.");
} catch (JSONException e) {
throw new RuntimeException(e);
}
Logger.e("permissionDenied");
}
});
Log.e("phone_number::",object.toString());
return object.toString();
}
주의 사항: 레이아웃으로 지정할 경우 레이아웃 파일명 : mcore_main_layout.xml (고정) android:id 설정값 ::: Header수정시에는 heaerLayout, Bottom수정시에는 bottomLayout으로 지정 (고정)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mainLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/heaerLayout"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentTop="true"
android:background="@android:color/holo_green_light" />
<LinearLayout
android:id="@+id/bottomLayout"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:background="@android:color/holo_red_dark" />
<m.client.android.library.core.customview.MPWebView
android:id="@+id/mcore_webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/bottomLayout"
android:layout_below="@id/heaerLayout"
android:layout_alignLeft="@id/leftLayout"
android:layout_alignRight="@id/rightLayout"
android:background="@android:color/holo_purple" />
<LinearLayout
android:layout_alignParentLeft="true"
android:id="@+id/leftLayout"
android:background="@android:color/holo_blue_dark"
android:layout_width="50dp"
android:layout_height="match_parent"/>
<LinearLayout
android:layout_alignParentRight="true"
android:id="@+id/rightLayout"
android:background="@android:color/holo_purple"
android:layout_width="50dp"
android:layout_height="match_parent"/>
</RelativeLayout>
commLibHandle.g_nBottomHeight = 120; // Pixel
commLibHandle.g_nHeaderHeight = 120; // Pixel
commLibHandle.processAppInit(this);
public class BaseActivity extends MainActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CommonLibHandler.getInstance().g_nBottomHeight = 500; // Pixel
CommonLibHandler.getInstance().g_nHeaderHeight = 200; // Pixel
}
public class Startup extends Activity{
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 리소스 업데이트 이후, cache clear 처리
CommonLibUtil.setVariableToStorage(LibDefinitions.strings.KEY_RESOURCE_UPDATE_CACHE_CLEAR, "true", this);
}
}