본문 바로가기
Tips

Android Studio로 만든 WebView에 광고(AdView) 삽입하기 - RPG Maker MV

by biud436 2018. 5. 5.


Don't try this. 


My post is pretty old and notice that it didn't work in a new Google Mobile Ads SDK that used com.google.android.gms:play-services-ads:18.3.0. 


The source code will be updated in the near future. However, The Android SDK will be changed frequently so my source code can be deprecated after time passes. 


Therefore, I'm still thinking about the way of source code accesses. 


But I'm not expert developer about Android so I can't manage the source code and some issues so I can't consider open access such as Github.


To show the Ads, I recommend you can use Cordova CLI instead of Android Studio.




빌드 법 중에 Android Studio로 빌드를 하는 방법이 있는데, 업데이트가 되면서 빌드가 잘 되고 있다. 이 빌드 법에서 AdMob 스마트 배너 광고와 전면 광고를 넣는 것을 테스트를 해봤다. 이것의 원리는 뷰그룹인 FrameLayout을 만들고 addView로 WebView와 AdView를 자식 객체로 추가하는 것이다. 이때 추가 순서가 중요한데 광고를 먼저 추가하게 되면 광고가 웹뷰에 의해 감춰지게 된다. 레이아웃을 만들고 setContentView()를 호출하면 커스텀으로 만든 레이아웃이 화면에 표시된다.



광고의 위치는 gravity 값을 설정하여 조절이 가능하며, SMART_BANNER를 설정하면 화면 크기에 맞게 광고가 조절된다. Gravity.BOTTOM으로 설정하면 광고가 화면 하단에 자동으로 부착된다. 아무것도 설정하지 않으면 상단에 설정된다.



전면 광고는 별도로 처리하지 않고 광고 ID 값과 광고 요청을 하면 별도로 설정하지 않아도 화면 위에 뜬다.



이렇게 자바로 작성된 광고 호출 코드는 자바스크립트 인터페이스를 통해 자바스크립트로 직접적인 호출이 가능하다. 예를 들면, androidAPI.ShowBannerAd()라는 자바스크립트는 배너 광고를 화면에 표시한다. 또한 androidAPI.ShowInterstitialAd()는 화면에 전면 광고를 표시한다. 광고 ID 값은 values.xml에서 BannerAd 값과 InterstitialAd를 적으면 바꿀 수 있게 했다.




기본 프로젝트를 애드몹 프로젝트로 변경하려면 전체 소스를 다운로드 받아야 한다.


다운로드 링크는 다음과 같다.


가이드 원문 - https://rpgmakermv.co/threads/android-rpg-maker-mv-guide.6763/

직접 다운로드 링크 -  https://github.com/AltimitSystems/mv-android-client/zipball/master


가이드에 따라 모든 프로젝트를 설정한 후,  파일 트리에서 build.gradle (Module:app) 파일을 찾아서 열면 하단에 dependencies라는 부분이 있다. 이 부분에  compile 'com.google.android.gms:play-services-ads:11.4.2' 라는 구문을 추가한다.  



구체적인 소스는 다음과 같다. 


1
2
3
4
5
6
7
8
9
10
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
 
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'com.google.android.gms:play-services-ads:18.3.0'
 
    zz_crosswalkImplementation 'org.xwalk:xwalk_core_library:23.53.589.4'
 
}
 
cs


그 후에는 AndroidManifest.xml 파일을 열고, 추가 권한을 설정해야 한다. 원래 기본적인 설정이 모두 되어있지만 애드몹 설정 시 퍼미션 오류로 인해 컴파일이 되지 않기 때문이다. 




스크린샷과 같은 부분에 추가한다. 이는 권한을 요구하는 부분이며 특정 권한에 대한 설명은 구글에 검색하면 모두 설명되어있다. 필요 없는 부분은 삭제해도 좋지만 "android.permission.INTERNET" 권한은 필수이다.


<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2017-2018 Altimit Community Contributors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="systems.altimit.rpgmakermv">

<application
android:label="@string/app_name"
android:icon="@mipmap/app_icon"
android:roundIcon="@mipmap/app_icon_round"
android:theme="@style/AppTheme"
android:allowBackup="true"
android:fullBackupContent="true">

<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713"/>

<activity
android:name=".WebPlayerActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:screenOrientation="sensorLandscape"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:scheme="@string/app_scheme"
android:host="@string/app_host" />
</intent-filter>
</activity>
</application>

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>

</manifest>


소스 내에 직접 문자열을 기입하는 것을 막기 위해 설정 파일에 광고 ID를 추가하고 그것을 불러오게 하려고 한다. 그럴려면 우선 values.xml 파일을 열고, BannerAd와 InterstitialAd 노드를 추가해야 한다. 해당 노드는 애드몹 사이트에서 만든 광고 ID를 기입해야 한다.


<string name="BannerAd">ca-app-pub-3940256099942544/6300978111</string>
<string name="InterstitialAd">ca-app-pub-3940256099942544/1033173712</string>
<string name="VideoAd">ca-app-pub-3940256099942544/5224354917</string>




앱의 시작점은 AndroidManifest.xml에서 알 수 있는데, WebPlayerActivity.java에 그 부분이 정의되어있다.




이 파일을 열려면 상단의 Android 콤보박스를 Project Files로 바꾸고, WebPlayerActivity.java 파일을 찾아서 열어야 한다.



해당 파일에 붉은 색으로 강조된 부분을 입력한다. 


▲ 패키지가 없어서 붉은색으로 나온 다면 ALT + ENTER 키로 임포트 할 수 있다. 


▲코드 입력 시에 자동으로 불러오게 하고 싶다면 File - Settings - Editor - Auto Import에서 다음을 체크해야 한다.


다음에는 프레임 레이아웃을 화면에 표시하고, 해당 뷰그룹에 웹뷰와 애드몹을 자식 객체로 추가한다. 또한 전면 광고를 화면에 띄운다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// ... 중략
    
public class WebPlayerActivity extends Activity {
    
    // ... 중략
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (BuildConfig.BACK_BUTTON_QUITS) {
            createQuitDialog();
        }
 
        mSystemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            mSystemUiVisibility |= View.SYSTEM_UI_FLAG_FULLSCREEN;
            mSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            mSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
            mSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
        }
 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            mSystemUiVisibility |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
        }
 
        mPlayer = PlayerHelper.create(this);
        mPlayer.setKeepScreenOn();
 
        if (!addBootstrapInterface(mPlayer)) {
            Uri.Builder projectURIBuilder = Uri.fromFile(new File(getString(R.string.mv_project_index))).buildUpon();
            projectURIBuilder.query(getString(R.string.query_noaudio));
            mPlayer.loadUrl(projectURIBuilder.build().toString());
        }
 
        init();
 
    }
 
    // ... 중략
 
    private boolean addBootstrapInterface(Player webView) {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
            new Bootstrapper(webView);
            return true;
        }
        return false;
    }
    
    public void init() {
 
        WEBPLAYER_ACTIVITY = this;
 
        // Main Layout
        ViewGroup.LayoutParams framelayoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
 
        ROOT_LAYOUT = null;
        ROOT_LAYOUT = new FrameLayout(this);
 
        ROOT_LAYOUT.setLayoutParams(framelayoutParams);
 
        // WebPlayer Layout
        FrameLayout.LayoutParams webViewLayoutParams = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT);
        ROOT_LAYOUT.addView(mPlayer.getView(), webViewLayoutParams);
 
        // AdView Layout
        FrameLayout.LayoutParams adViewLayoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
 
        adViewLayoutParams.gravity = Gravity.BOTTOM;
 
        WEBPLAYER_ADVIEW = null;
        WEBPLAYER_ADVIEW = new AdView(this);
        WEBPLAYER_ADVIEW.setLayoutParams(adViewLayoutParams);
        WEBPLAYER_ADVIEW.setAdSize(AdSize.SMART_BANNER);
        WEBPLAYER_ADVIEW.setAdUnitId(getString(R.string.BannerAd));
 
        mInterstitialAd = newInterstitialAd();
 
        mAdRequest = new AdRequest.Builder()
                .addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
                .build();
 
        WEBPLAYER_ADVIEW.setVisibility(View.GONE);
 
        WEBPLAYER_ADVIEW.loadAd(mAdRequest);
 
        ROOT_LAYOUT.addView(WEBPLAYER_ADVIEW);
 
        setContentView(ROOT_LAYOUT);
 
    }
 
    private InterstitialAd newInterstitialAd() {
        InterstitialAd interstitialAd = new InterstitialAd(this);
        interstitialAd.setAdUnitId(getString(R.string.InterstitialAd));
        interstitialAd.setAdListener(new AdListener() {
            @Override
            public void onAdLoaded() {
                showInterstitial();
            }
 
            @Override
            public void onAdFailedToLoad(int errorCode) {
 
            }
 
            @Override
            public void onAdClosed() {
 
            }
 
        });
        return interstitialAd;
    }
 
    public void loadInterstitial() {
        AdRequest adRequest = new AdRequest.Builder()
                .setRequestAgent("android_studio:ad_template").build();
        mInterstitialAd.loadAd(adRequest);
    }
 
    public void showInterstitial() {
        if ( mInterstitialAd != null && mInterstitialAd.isLoaded() ) {
            mInterstitialAd.show();
        } else {
            mInterstitialAd = newInterstitialAd();
            loadInterstitial();
        }
    }
 
    private static final class Bootstrapper extends PlayerHelper.Interface implements Runnable {
        // ... 중략
    }
}
 
        
cs


다음은 자바스크립트 인터페이스를 추가하고, 메인 엑티비티에 접근하는 코드이며 기본 웹뷰에 대한 파일 WebPlayerView.java를 수정한 것이다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// .... 중략
 
public class WebPlayerView extends WebView {
 
    // .... 중략
 
    private WebPlayer mPlayer;
    private Handler mHandler = new Handler();
 
    // .... 중략
    
    private void init(Context context) {
        mPlayer = new WebPlayer(this);
 
        setBackgroundColor(Color.BLACK);
 
        WebSettings webSettings = getSettings();
        enableJavascript();
        webSettings.setAllowContentAccess(true);
        webSettings.setAllowFileAccess(true);
        webSettings.setLoadsImagesAutomatically(true);
        webSettings.setDomStorageEnabled(true);
        webSettings.setAppCacheEnabled(true);
        webSettings.setDatabaseEnabled(true);
 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            webSettings.setAllowFileAccessFromFileURLs(true);
            webSettings.setAllowUniversalAccessFromFileURLs(true);
        }
 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            setLayerType(View.LAYER_TYPE_HARDWARE, null);
        } else {
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
 
        mPlayer.addJavascriptInterface(new JavascriptMethods(), "androidAPI");
 
        setWebChromeClient(new ChromeClient());
        setWebViewClient(new ViewClient());
    }    
    
    // .... 중략
    
    final class JavascriptMethods {
        JavascriptMethods() {
 
        }
        
        @android.webkit.JavascriptInterface
        public void HideBannerAd() {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    WebPlayerActivity.WEBPLAYER_ADVIEW.setVisibility(View.GONE);
                }
            });
        }
 
        @android.webkit.JavascriptInterface
        public void ShowBannerAd() {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    WebPlayerActivity.WEBPLAYER_ADVIEW.setVisibility(View.VISIBLE);
                }
            });
        }
 
        @android.webkit.JavascriptInterface
        public void ShowInterstitialAd() {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    WebPlayerActivity.WEBPLAYER_ACTIVITY.loadInterstitial();
                }
            });
        }
 
    }    
    
}
cs


다음은 자바스크립트 인터페이스를 추가하고, 메인 엑티비티에 접근하는 코드이며 크로스워크 웹뷰에 대한 파일 XWalkPlayerView.java를 수정한 것이다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// .... 중략
 
public class XWalkPlayerView extends XWalkView {
 
    // .... 중략
 
    private XWalkPlayer mPlayer;
    private Handler mHandler = new Handler();
 
    // .... 중략
    
    private void init(final Context context) {
        mPlayer = new XWalkPlayer(this);
 
        setBackgroundColor(Color.BLACK);
 
        XWalkSettings webSettings = getSettings();
        enableJavascript();
        webSettings.setAllowContentAccess(true);
        webSettings.setAllowFileAccess(true);
        webSettings.setLoadsImagesAutomatically(true);
        webSettings.setDomStorageEnabled(true);
        webSettings.setDatabaseEnabled(true);
 
        // 원격 디버깅 설정
        XWalkPreferences.setValue(XWalkPreferences.REMOTE_DEBUGGING, true);
 
        // window.androidAPI로 접근...
        mPlayer.addJavascriptInterface(new JavascriptMethods(), "androidAPI");
 
 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            webSettings.setAllowFileAccessFromFileURLs(true);
            webSettings.setAllowUniversalAccessFromFileURLs(true);
        }
 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            setLayerType(View.LAYER_TYPE_HARDWARE, null);
        } else {
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
    }
    
    // .... 중략
    
 
    final class JavascriptMethods {
        JavascriptMethods() {
 
        }
        
        @JavascriptInterface
        public void HideBannerAd() {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    WebPlayerActivity.WEBPLAYER_ADVIEW.setVisibility(View.GONE);
                }
            });
        }
 
        @JavascriptInterface
        public void ShowBannerAd() {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    WebPlayerActivity.WEBPLAYER_ADVIEW.setVisibility(View.VISIBLE);
                }
            });
        }
 
        @JavascriptInterface
        public void ShowInterstitialAd() {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    WebPlayerActivity.WEBPLAYER_ACTIVITY.loadInterstitial();
                }
            });
        }
 
    }
    
}
cs



Reference


MV Android - https://github.com/AltimitSystems/mv-android-client


AdMob Banner Ads - Mobile Ads Garage - https://www.youtube.com/watch?v=h-FMndW2kHo&list=LLmDjEy9UvrppOdTFmwrfzcQ&index=7


프레임 레이아웃 생성 및 추가 - https://github.com/cocos2d/cocos2d-x/blob/0d37220bebfc0508d13ee4ea1e89726330cea730/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxActivity.java#L241


InterstitialAd 생성 - https://stackoverflow.com/a/33492137