Default interface methods are only supported starting with Android N (--min-api 24): androidx.lifecycle.Lifecycle androidx.lifecycle.LifecycleRegistryOwner.getLifecycle()

 

아래처럼 해주면 해결된다.

 

Gradle Scripts > build.gradle ( Module:app )

android	{
	...
    compileOptions {
    	sourceCompatibility JavaVersion.VERSION_1_8
    	targetCompatibility JavaVersion.VERSION_1_8
	}
}

1. 인터넷을 연결되어야 하는 경우 manifest에서 internet 권한을 추가해 줘야한다.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.saii.webview">

    <application> ...
    </application>
	<uses-permission android:name="android.permission.INTERNET" />
</manifest>

 


2. assests 폴더를 생성한다.

New > Folder > Assets Folder 아래 이미지 참조.


3. HTML은 할 줄 몰라서 간단하게 만들었습니다.

index.html

<!DOCTYPE html>
<html>
<head>
    <title>Saii WebView Example</title>
</head>

<script type="text/javascript">
        function showAndroidToast(toast) {
           var word = document.createElement('div');
           word.textContent = Saii.showToast(toast);
          document.body.append(word);
        }
</script>

	<body>
		<h1 id="webview-example">Saii WebView - Example</h1>

    	<input id="keyword" type="text" placeholder="keyword">
    	<br>
    	<button type="submit" onClick="showAndroidToast(keyword.value)">Submit</button>
	</body>
</html>

4. 메인 화면에 WebView 하나를 배치한다.

activity_main.xml

<?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"
    tools:context=".MainActivity">

    <WebView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/wv_main"
        tools:layout_editor_absoluteX="1dp"
        tools:layout_editor_absoluteY="1dp" />
</androidx.constraintlayout.widget.ConstraintLayout>


5. 마지막으로 WebView를 Html 또는 다른 웹 페이지와 연결을 시켜준다.

MainActvity.java

package com.saii.webview;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MainActivity extends AppCompatActivity {
    private WebView mWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mWebView = (WebView) findViewById(R.id.wv_main);

        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);

        //mWebView.loadUrl("https://saii42.tistory.com/97");
        mWebView.loadUrl("file:///android_asset/index.html");
    }
}

 

참고

https://github.com/saii42/android/tree/main/example/WebView

 

GitHub - saii42/android: android

android. Contribute to saii42/android development by creating an account on GitHub.

github.com

 

왠만한거 다 세팅했는데 공개 테스트에 App Bundle을 올리고 버전 검토를 누르고 기다리는데 아래 처럼 오류가 발생했다. 

 

앱에 버전 코드 1의 APK가 있으므로 android.permission.CAMERA, android.permission.RECORD_AUDIO, android.permission.READ_PHONE_STATE, android.permission.READ_CONTACTS 권한이 필요합니다. APK에서 이 권한을 사용하는 앱에는 개인정보처리방침을 설정해야 합니다.

 

대충 먼소리냐 특정 권한을 사용하려면 개인정보처리방침 URL을 등록해야된다.

 

콘솔이 예전이랑 많이 달라져서 당황했다. 처음 세팅할 때 나왔어야 했는데 앱을 업로드하고 검토 누르니깐 대시보드에 생겼다. 개인정보처리방침을 URL에 등록하자.

 

java.io.FileNotFoundException: /storage/emulated/0/DCIM/Camera/20201021_135624.jpg: open failed: EACCES(Permission denied)

테스트 도중 이런 에러가 발생했습니다. 파일 업로드 중에 발생했는데 이유를 알아보니 파일 생성 시 저장소에 관한 내용이었습니다.

앱을 삭제해도 앱 파일들은 삭제되지만 외부저장소에 있는 파일들은 앱 제거시 삭제가 되지 않는다는 것입니다.

즉 안드로이드는 앱을 제거해도 내부에 파일이 남기 때문에 구글은 이것을 보안하려고 하는 것 같습니다.

아직까지 이 부분에 대해서 많은 개발자들이 코드를 적용하지 못한 거 같아서 당장 없애진 않았네요 구글도 이 부분을 인지하고 아직 방법을 남겨 뒀습니다.

manifest에 requestLegacyExternalStorage 속성을 true로 해줍니다.

<application 
...
    android:requestLegacyExternalStorage="true" ... >
        

developer.android.com/training/data-storage/files/external-scoped

 

앱을 잘 만들고 있는 도중 에러가 발생했다.

network security policy 라고 하는 것 보니깐 네트워크 보안 정책이 바뀐 것 같다.

잘은 모르지만 기본적으론 HTTP 통신을 기본으로 허용했지만 결론적으로 HTTP 통신을 차단을 했습니다.

MANIFEST 속성이 하나 추가됐습니다. 바로 android:usesCleartextTraffic 입니다.

이 속성이 누가 버전부터 추가된 모양입니다. 저 속성은 기본 값이 FALSE 인듯 합니다.

 

그래서 가장 쉬운 해결방법은 API의 URL을 HTTP에서 HTTPS로 변경하면 됩니다.

사실 이게 쉬웠으면 좋겠지만 안드로이드 개발자 입장에서 쉽다고 할 수는 없겠죠...

다른 해결방법도 있습니다.

네트워크 보안 구성 파일 (newwork_security_config)을 만들어줍니다.

res/xml/network_security_config.xml

<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">insecure.example.com</domain>
        <domain includeSubdomains="true">insecure.cdn.example.com</domain>
    </domain-config>
</network-security-config>

그리고  manifest에 추가 해줍니다. 

<application
    ...
    android:networkSecurityConfig="@xml/network_security_config">

 

android-developers.googleblog.com/2018/04/protecting-users-with-tls-by-default-in.html

 

이런 방법도 있는데 정말 간단한 방법도 있더라고요. 그렇지만 공식 문서에서는 추천하지 않고 있습니다.

<application
    ...
   android:usesCleartextTraffic="true">

문제는 없었는데 보안상에는 안좋다고 나와있네요.

developer.android.com/guide/topics/manifest/application-element#usesCleartextTraffic

 

 

android-developers.googleblog.com/2018/04/protecting-users-with-tls-by-default-in.html

developer.android.com/guide/topics/manifest/application-element#usesCleartextTraffic

안드로이드 개발 도중 android.content.res.Resources$NotFoundException:String resource ID #0x1 라는 에러가 발생했습니다.

TextView에 setText() 함수를 사용할 때 integer 타입의 데이터를 넣어서 발생하는 에러입니다.

보통은 에러가 발행하면 바로 코드에 빨간 밑줄이 발생합니다.

그렇지만 다른 함수에서 리턴을 받는 경우는 빨간 밑줄 그러니깐 에러가 발생하지 않습니다.

해결 방법은 엄청 간단합니다. 

Int 값을 String 값으로 바꿔주면 간단합니다.

TextInputEditText hint

TextInputEditText 클릭하면 힌트가 위로 올라가면서 작아집니다.

우선 이 색상을 선택하려면 TextInputLayout 태그에서 app:hintTextColor 속성을 선택해주면 됩니다.

app:hintTextColor="@color/color_4b6d9d" 이런식으로 속성을 정의 해주시면 됩니다.

 

TextInputEditText 커서 

우선 조금 특이한점은 color로 설정하는 것이 아닌 drawable로 설정해야 합니다. 

즉 xml 파일입니다. 그리 어려운건 아닙니다. 밑에 처럼 xml 파일 만들고 코딩하면 됩니다. 색상도 color로 속성으로 지정하면 됩니다.

TextInputEditText 안에 android:textCursorDrawable="@drawable/cursor_color" 이런식으로 속성을 정의 해주시면 됩니다. 

 

TextInputEditText 밑줄

밑줄은 TextInputEditText 태그에서 android:backgroundTint="@color/color_4b6d9d" 속성을 정의해주시면 됩니다.

 

 

정리하자면 밑에 그림을 참고하시면 됩니다.

 

 

values > style.xml 에서

우선 <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

DarkActionBar > NoActionBar로 바꿔줍니다.

 

menu 폴더 밑에 xml 파일 하나 만들어줍니다.

toolbar.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/toolbar_download_button"
        android:title="다운로드"
        android:icon="@drawable/ic_file_download_black_24dp"
        app:showAsAction="ifRoom"/>
</menu>

 

activity_main.xml

<?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">
    
    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appBarLayout2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/toolbar_title"
                android:text="toolbar"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
                android:layout_gravity="center" />

        </androidx.appcompat.widget.Toolbar>
    </com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

MainActivity.java

OnCreate() 메서드에서 toolbar와 actionBar 설정을 해줍니다.

Ctrl + O 를 눌러서 onCreateOptionsMenu(), onOptionsItemSelected() 를 선언해줍니다.

public class BlockchainStorageDetailActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_blockchain_storage_detail);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        ActionBar actionBar = getSupportActionBar();
        actionBar.setDisplayShowTitleEnabled(false); // 기존 title 지우기
        actionBar.setDisplayHomeAsUpEnabled(true); // 뒤로가기 버튼 만들기
    }





    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater menuInflater = getMenuInflater();
        menuInflater.inflate(R.menu.toolbar_blockchain_storage_download, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()){
            case R.id.toolbar_download_button:{
                break;
            }
            case android.R.id.home:
                finish();
                break;
        }
        return super.onOptionsItemSelected(item);
    }
}

onOptionsItemSelected() 메서드는 클릭 이벤트이니 case 선언 하고 이벤트 작성하시면 됩니다.

 

+ Recent posts