<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.packagename">
<queries>
...
</queries>
<uses-permission android:name="android.permission.INTERNET" />
<!-- 네트워크 상태 퍼미션 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- 카메라 퍼미션 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CAMERA2" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<!-- 5.0 버전 파일업로드 퍼미션 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="18"/>
<!-- 외부 저장소 사용 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:hardwareAccelerated="true"
android:icon="@mipmap/alldeal_launch"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:supportsRtl="true"
android:theme="@style/Theme.Alldeal"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<activity
android:name=".IntroActivity"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:exported="true"
android:padding="0dp" />
<service
android:name=".MyFireBaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
}
** res/xml/file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="sdcard"
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="." />
</paths>
** res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true"/>
</network-security-config>
package com.majorworld.alldeal;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.Intent;
import android.graphics.Bitmap;
import android.content.DialogInterface;
//import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Parcelable;
import android.provider.MediaStore;
import android.provider.Settings;
import android.util.Log;
import android.view.KeyEvent;
import android.webkit.JsResult;
import android.webkit.ValueCallback;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.Toast;
import java.io.File;
//import com.google.android.gms.tasks.OnCompleteListener;
//import com.google.android.gms.tasks.Task;
//import com.google.firebase.iid.FirebaseInstanceId;
//import com.google.firebase.iid.InstanceIdResult;
//import com.google.firebase.iid.FirebaseInstanceId;
//import com.google.firebase.iid.InstanceIdResult;
import java.net.URISyntaxException;
public class MainActivity extends AppCompatActivity {
// ------------------------- SETTING -------------------------- //
private static final String TAG = "MainActivityLog";
private WebView webView1;
private ProgressBar pBar;
private String defaultUrl = "https://test123.co.kr";
private WebSettings webSettings;
private long time = 0;
public ValueCallback<Uri> filePathCallbackNormal;
public ValueCallback<Uri[]> filePathCallbackLollipop;
public final static int FILECHOOSER_NORMAL_REQ_CODE = 2001;
public final static int FILECHOOSER_LOLLIPOP_REQ_CODE = 2002;
private Uri cameraImageUri = null;
// ------------------------- ONCREATE -------------------------- //
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView1 = (WebView) findViewById(R.id.wView);
pBar = findViewById(R.id.pBar);
// 각종 권한 획득
checkVerify();
webSettings = webView1.getSettings();
webSettings.setJavaScriptEnabled(true); // 자바스크립트 사용
webSettings.setSupportMultipleWindows(true); // 새창 띄우기 허용
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 자바스크립트 새창 띄우기 허용
webSettings.setLoadWithOverviewMode(true); // 메타태그 허용
webSettings.setUseWideViewPort(true); // 화면 사이즈 맞추기 허용
webSettings.setSupportZoom(false); // 화면줌 허용 여부
webSettings.setBuiltInZoomControls(false); // 화면 확대 축소 허용 여부
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); // 컨텐츠 사이즈 맞추기
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE); // 브라우저 노캐쉬
webSettings.setDomStorageEnabled(true); // 로컬저장소 허용
webSettings.setAllowUniversalAccessFromFileURLs(true);
webSettings.setSaveFormData(false);
webSettings.setTextZoom(95);
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
webView1.loadUrl(defaultUrl);
webView1.setWebChromeClient(new WebChromeClientClass()); //웹뷰에 크롬 사용 허용. 이 부분이 없으면 크롬에서 alert가 뜨지 않음
webView1.setWebViewClient(new WebViewClientClass());
}
// ------------------------- BACK PRESSED -------------------------- //
@Override
public void onBackPressed() {
//super.onBackPressed();
if(System.currentTimeMillis()-time>=2000){
time=System.currentTimeMillis();
Toast.makeText(getApplicationContext(),"뒤로 버튼을 한번 더 누르면 종료합니다.",Toast.LENGTH_SHORT).show();
}else if(System.currentTimeMillis()-time<2000){
finish();
return;
}
}
// ------------------------- CAMERA ACTIVATE & FILE UPLOAD -------------------------- //
//권한 획득 여부 확인
@TargetApi(Build.VERSION_CODES.M)
public void checkVerify() {
if (ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_NETWORK_STATE) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
//Log.d("checkVerify() : ","if문 들어옴");
//카메라 또는 저장공간 권한 획득 여부 확인
if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) || ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA)) {
Toast.makeText(getApplicationContext(),"권한 관련 요청을 허용해 주셔야 카메라 캡처이미지 사용등의 서비스를 이용가능합니다.",Toast.LENGTH_SHORT).show();
} else {
//Log.d("checkVerify() : ","카메라 및 저장공간 권한 요청");
// 카메라 및 저장공간 권한 요청
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.INTERNET, Manifest.permission.CAMERA,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
}
}
}
//권한 획득 여부에 따른 결과 반환
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//Log.d("onRequestPermissionsResult() : ","들어옴");
if (requestCode == 1)
{
if (grantResults.length > 0)
{
for (int i=0; i<grantResults.length; ++i)
{
if (grantResults[i] == PackageManager.PERMISSION_DENIED)
{
// 카메라, 저장소 중 하나라도 거부한다면 앱실행 불가 메세지 띄움
new AlertDialog.Builder(this).setTitle("알림").setMessage("권한을 허용해주셔야 앱을 이용할 수 있습니다.")
.setPositiveButton("종료", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
finish();
}
}).setNegativeButton("권한 설정", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.parse("package:" + getApplicationContext().getPackageName()));
getApplicationContext().startActivity(intent);
}
}).setCancelable(false).show();
return;
}
}
//Toast.makeText(this, "Succeed Read/Write external storage !", Toast.LENGTH_SHORT).show();
}
}
}
//액티비티가 종료될 때 결과를 받고 파일을 전송할 때 사용
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//Log.d("onActivityResult() ","resultCode = " + Integer.toString(requestCode));
switch (requestCode)
{
case FILECHOOSER_NORMAL_REQ_CODE:
if (resultCode == RESULT_OK)
{
if (filePathCallbackNormal == null) return;
Uri result = (data == null || resultCode != RESULT_OK) ? null : data.getData();
// onReceiveValue 로 파일을 전송한다.
filePathCallbackNormal.onReceiveValue(result);
filePathCallbackNormal = null;
}
break;
case FILECHOOSER_LOLLIPOP_REQ_CODE:
//Log.d("onActivityResult() ","FILECHOOSER_LOLLIPOP_REQ_CODE = " + Integer.toString(FILECHOOSER_LOLLIPOP_REQ_CODE));
if (resultCode == RESULT_OK)
{
//Log.d("onActivityResult() ","FILECHOOSER_LOLLIPOP_REQ_CODE 의 if문 RESULT_OK 안에 들어옴");
if (filePathCallbackLollipop == null) return;
if (data == null)
data = new Intent();
if (data.getData() == null)
data.setData(cameraImageUri);
filePathCallbackLollipop.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
filePathCallbackLollipop = null;
}
else
{
//Log.d("onActivityResult() ","FILECHOOSER_LOLLIPOP_REQ_CODE 의 if문의 else문 안으로~");
if (filePathCallbackLollipop != null)
{ // resultCode에 RESULT_OK가 들어오지 않으면 null 처리하지 한다.(이렇게 하지 않으면 다음부터 input 태그를 클릭해도 반응하지 않음)
//Log.d("onActivityResult() ","FILECHOOSER_LOLLIPOP_REQ_CODE 의 if문의 filePathCallbackLollipop이 null이 아니면");
filePathCallbackLollipop.onReceiveValue(null);
filePathCallbackLollipop = null;
}
if (filePathCallbackNormal != null)
{
filePathCallbackNormal.onReceiveValue(null);
filePathCallbackNormal = null;
}
}
break;
default:
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
// 카메라 기능 구현
private void runCamera(boolean _isCapture)
{
Intent intentCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//intentCamera.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//File path = getFilesDir();
File path = Environment.getExternalStorageDirectory();
File file = new File(path, "sample.png"); // sample.png 는 카메라로 찍었을 때 저장될 파일명이므로 사용자 마음대로
// File 객체의 URI 를 얻는다.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
String strpa = getApplicationContext().getPackageName();
cameraImageUri = FileProvider.getUriForFile(this, strpa + ".fileprovider", file);
}
else
{
cameraImageUri = Uri.fromFile(file);
}
intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, cameraImageUri);
if (!_isCapture)
{ // 선택팝업 카메라, 갤러리 둘다 띄우고 싶을 때
Intent pickIntent = new Intent(Intent.ACTION_PICK);
pickIntent.setType(MediaStore.Images.Media.CONTENT_TYPE);
pickIntent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
String pickTitle = "사진 가져올 방법을 선택하세요.";
Intent chooserIntent = Intent.createChooser(pickIntent, pickTitle);
// 카메라 intent 포함시키기..
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{intentCamera});
startActivityForResult(chooserIntent, FILECHOOSER_LOLLIPOP_REQ_CODE);
}
else
{// 바로 카메라 실행..
startActivityForResult(intentCamera, FILECHOOSER_LOLLIPOP_REQ_CODE);
}
}
// ------------------------- ONKEY DOWN -------------------------- //
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode==KeyEvent.KEYCODE_BACK) && webView1.canGoBack()) {
webView1.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
// ------------------------- CHROME CLIENT -------------------------- //
private class WebChromeClientClass extends WebChromeClient {
// 자바스크립트의 alert창
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
new AlertDialog.Builder(view.getContext())
.setTitle("Alert")
.setMessage(message)
.setPositiveButton(android.R.string.ok,
new AlertDialog.OnClickListener(){
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
})
.setCancelable(false)
.create()
.show();
return true;
}
// 자바스크립트의 confirm창
@Override
public boolean onJsConfirm(WebView view, String url, String message,
final JsResult result) {
new AlertDialog.Builder(view.getContext())
.setTitle("Confirm")
.setMessage(message)
.setPositiveButton("Yes",
new AlertDialog.OnClickListener(){
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
})
.setNegativeButton("No",
new AlertDialog.OnClickListener(){
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
})
.setCancelable(false)
.create()
.show();
return true;
}
// For Android 5.0+ 카메라 - input type="file" 태그를 선택했을 때 반응
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public boolean onShowFileChooser(
WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
//Log.d("TAG", "5.0+");
// Callback 초기화 (중요!)
if (filePathCallbackLollipop != null) {
filePathCallbackLollipop.onReceiveValue(null);
filePathCallbackLollipop = null;
}
filePathCallbackLollipop = filePathCallback;
boolean isCapture = fileChooserParams.isCaptureEnabled();
//Log.d("onShowFileChooser : " , String.valueOf(isCapture));
runCamera(isCapture);
return true;
}
}
// ------------------------- WEBVIEW CLIENT (INTENT SETTING) -------------------------- //
private class WebViewClientClass extends WebViewClient {
@Override // 1) Loading
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
pBar.setVisibility(View.VISIBLE); // Show Loading
}
@Override // 2) Start
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
pBar.setVisibility(View.GONE); // Hide Loading
}
@TargetApi(Build.VERSION_CODES.N)
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
//Log.d("WebViewClient URL : " , request.getUrl().toString());
if (request.getUrl().getScheme().equals("intent")) {
try {
// Intent 생성
Intent intent = Intent.parseUri(request.getUrl().toString(), Intent.URI_INTENT_SCHEME);
// 실행 가능한 앱이 있으면 앱 실행
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
//Log.d(TAG, "ACTIVITY: " + intent.getPackage());
return true;
}
// Fallback URL이 있으면 현재 웹뷰에 로딩
String fallbackUrl = intent.getStringExtra("browser_fallback_url");
if (fallbackUrl != null) {
view.loadUrl(fallbackUrl);
//Log.d(TAG, "FALLBACK: " + fallbackUrl);
return true;
}
// 앱이 설치되어 있지 않으면 마켓으로 이동
Intent marketIntent = new Intent(Intent.ACTION_VIEW);
//카카오링크 예외처리
String packageName;
if(request.getUrl().toString().contains("kakaolink")){
packageName = "com.kakao.talk";
}else{
packageName = intent.getPackage();
}
marketIntent.setData(Uri.parse("market://details?id=" + packageName));
if (marketIntent.resolveActivity(getPackageManager()) != null) {
startActivity(marketIntent);
//Log.d(TAG, "MARKET: " + intent.getPackage());
return true;
}
//Log.e(TAG, "Could not parse anything");
} catch (URISyntaxException e) {
//Log.e(TAG, "Invalid intent request", e);
}
}
view.loadUrl(request.getUrl().toString());
return true;
//return super.shouldOverrideUrlLoading(view, request);
}
}
//----------------------------------- ORIGINAL CODE FOR REFERENCE ------------------------------//
/*
private static final String TAG = "MainActivityLog";
// ------------------------- VAR -------------------------- //
WebView wView;
ProgressBar pBar;
String defaultUrl = "https://alldeal.kr";
// ------------------- DeviceControl ---------------------- //
@Override
public void onBackPressed() {
if(wView.canGoBack()){ // if IS BACK PAGE
wView.goBack(); // GO BACK
}else{
super.onBackPressed(); // IF NOT BACK PAGE -> EXIT APPLICATION
}
}
// ----------------------- ONCREATE ------------------------ //
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //base layout
wView = findViewById(R.id.wView);
pBar = findViewById(R.id.pBar);
setWebView();
wView.loadUrl(defaultUrl);
*/
/* firebase connect code
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
@Override
public void onComplete(@NonNull Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
Log.w(TAG, "getInstanceId failed", task.getException());
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
// Log and toast
//String msg = getString(R.string.msg_token_fmt, token);
Log.d(TAG, token);
//Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
});
*/
/*
}
// ------------------------ SETTING ------------------------ //
public void setWebView(){
// 1. Client Setting
//wView.setWebChromeClient(new WebChromeClient());
wView.setWebViewClient(new WebViewClient() {
@Override // 1) Loading
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
pBar.setVisibility(View.VISIBLE); // Show Loading
}
@Override // 2) Start
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
pBar.setVisibility(View.GONE); // Hide Loading
}
@TargetApi(Build.VERSION_CODES.N)
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
Log.d(TAG, request.getUrl().toString());
if (request.getUrl().getScheme().equals("intent")) {
try {
// Intent 생성
Intent intent = Intent.parseUri(request.getUrl().toString(), Intent.URI_INTENT_SCHEME);
// 실행 가능한 앱이 있으면 앱 실행
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
Log.d(TAG, "ACTIVITY: " + intent.getPackage());
return true;
}
// Fallback URL이 있으면 현재 웹뷰에 로딩
String fallbackUrl = intent.getStringExtra("browser_fallback_url");
if (fallbackUrl != null) {
view.loadUrl(fallbackUrl);
Log.d(TAG, "FALLBACK: " + fallbackUrl);
return true;
}
// 앱이 설치되어 있지 않으면 마켓으로 이동
Intent marketIntent = new Intent(Intent.ACTION_VIEW);
//카카오링크 예외처리
String packageName;
if(request.getUrl().toString().contains("kakaolink")){
packageName = "com.kakao.talk";
}else{
packageName = intent.getPackage();
}
marketIntent.setData(Uri.parse("market://details?id=" + packageName));
if (marketIntent.resolveActivity(getPackageManager()) != null) {
startActivity(marketIntent);
Log.d(TAG, "MARKET: " + intent.getPackage());
return true;
}
Log.e(TAG, "Could not parse anything");
} catch (URISyntaxException e) {
Log.e(TAG, "Invalid intent request", e);
}
}
// 나머지 서비스 로직 구현
return false;
}
});
// 2. WebView Settings
WebSettings webSettings = wView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setUseWideViewPort(true);
webSettings.setLoadWithOverviewMode(true);
webSettings.setBuiltInZoomControls(false);
webSettings.setSupportZoom(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webSettings.setSupportMultipleWindows(true);
webSettings.setSaveFormData(false);
webSettings.setSavePassword(false);
webSettings.setLoadWithOverviewMode(true);
webSettings.setDomStorageEnabled(true);
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
webSettings.setEnableSmoothTransition(true);
webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH);
webSettings.setTextZoom(100);
}
*/
}