(다음 포스팅을 참고 했습니다. -> https://parkho79.tistory.com/12)

 

 

백그라운드 상태에서 (앱이 완전히 종료된 상태)

서비스를 실행할 수 있는 이벤트를 주었더니 다음과 같은 에러문이 나왔다.

Caused by: java.lang.IllegalStateException: Not allowed to start service Intent

 

찾아보니, Android O 버전 이상부터는 백그라운드 실행이 제한되었다고 한다.

그래서 Foreground service 를 사용해야한다.

 

관련 Android Developers 문서는 다음과 같다.

https://developer.android.com/about/versions/oreo/background?hl=ko#services

 

백그라운드 실행 제한  |  Android 개발자  |  Android Developers

Android 8.0 이상을 대상으로 하는 앱에 대한 새로운 백그라운드 제한.

developer.android.com

 

서비스를 호출해야한다면, 다음과 같이 코드를 변경해야한다.

1. 서비스 호출

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    context.startForegroundService(new Intent(context, TestService.class));
} else {
    context.startService(new Intent(context, TestService.class));
}

2. 서비스 실행

public class TestService extends Service
{
    @Override
    public void onCreate() {
        super.onCreate();
 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            final String strId = getString(R.string.noti_channel_id);
            final String strTitle = getString(R.string.app_name);
            NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            NotificationChannel channel = notificationManager.getNotificationChannel(strId);
            if (channel == null) {
                channel = new NotificationChannel(strId, strTitle, NotificationManager.IMPORTANCE_HIGH);
                notificationManager.createNotificationChannel(channel);
            }
 
            Notification notification = new NotificationCompat.Builder(this, strId).build();
            startForeground(1, notification);
        }
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            stopForeground(true);
        }
    }
}

 

 

The given artifact contains a string literal with a package reference 'Android.support.v4.content' that cannot be safely rewritten. Libraries using reflection such as annotation processors need to be updated manually to add support for androidx.

 

환경 : Butterknife 사용

오류 원인 : Butterknife 가 최신 버전이 아니라서 andoidx 를 지원해주지 않음.

해결방안 : 

Build(.app) 에 다음과 같이 Butterknife 를 최신 버전으로 업그레이드.

dependencies {
	implementation 'com.jakewharton:butterknife:10.0.0'
	annotationProcessor 'com.jakewharton:butterknife-compiler:10.0.0'
}

 

+) 최신 Android Studio 에서는 complie 대신에 implementation을 사용하라는 에러가 발생하기 시작함

+) 기존의 Butterknife 를 사용한 블로거들의 포스팅 날짜를 잘 살펴봐야함!! 그들의 포스팅이 도움은 되나, 2020 최신 포스팅이 아니기 때문에 Butterknife 버전도 다르고, Butterknife의 버전을 높였을 때의 사용하는 메서드도 달라짐ㅜ 결국 Butterknife를 버전 업그레이드 했다면, 기존에 썻던 Butterknife 관련 메서드들을 바꿔야함.

.... 살면서 이렇게 조급하게 삽질 해봤을까 싶다.

결론은 기본에 충실해야한다는 것을 깨달았다......

 

 

1. Manifest.xml - Permission 정의

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver
            android:name=".MyReceiver"
            android:exported="true"
            android:enabled="true">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE"></action>
            </intent-filter>
        </receiver>

    </application>

    <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
    <uses-permission android:name="android.permission.READ_CALL_LOG"></uses-permission>

</manifest>

https://developer.android.com/reference/android/telephony/TelephonyManager#EXTRA_INCOMING_NUMBER

다음의 Android Developer에 명시되어있었다.

이전까지는 READ_PHONE_STATE READ_CALL_LOG 를 permission을 허용해야한다는 것을..... 

 

 

2. MyReceiver.java - Broadcast Receiver 명시

public class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(final Context context, Intent intent) {
        // 리시버 상태 체크
        //Toast.makeText(context, "Event !!!", Toast.LENGTH_SHORT).show();

        TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);
        String phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);

        if (phoneNumber == null ) {
            Log.i("call-state", " : NULL");
        } else {
            if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(state)) {
                Toast.makeText(context, "call Active", Toast.LENGTH_SHORT).show();
            } else if (TelephonyManager.EXTRA_STATE_IDLE.equals(state)) {
                Toast.makeText(context, "No call", Toast.LENGTH_SHORT).show();
            } else if (TelephonyManager.EXTRA_STATE_RINGING.equals(state)) {
                Toast.makeText(context, "Ringing State", Toast.LENGTH_SHORT).show();
                Toast.makeText(context, phoneNumber, Toast.LENGTH_SHORT).show();
            }
        }

    }
}

if (phoneNumber == null ) 은 수신이 두 번 호출될 때를 위해서 사용한다.

if문 없이 Toast로 띄워보면 알겠지만, 수신이 2번 호출이 된다.

그래서 처음에는 공백으로, 두번째에 되서야 전화번호를 읽어온다.

그렇기 때문에 처음에 phoneNumber가 공백일때를 처음 if문 처리를 해주면 된다.

 

 

3. 앱에서의 Permission 허용하기

다음과 같이 실제로 앱에 permission을 줘야한다.

직접적으로 이렇게 눌러서 권한을 주는 방법도 있지만,

 

 

선호하는 방법은 사용자에게 권한을 부여해달라고 직접 코드를 짜는 것 이다. (사용자에게 편리하도록..ㅋㅋ)

사용자에게 권한을 주는 코드는 다음과 같다.

 

MainActivity.java

public class MainActivity extends AppCompatActivity {

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

        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED
                || ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED)
        {
            ActivityCompat.requestPermissions(MainActivity.this
                    , new String[]{Manifest.permission.READ_CALL_LOG
                            , Manifest.permission.READ_PHONE_STATE}
                            ,1);
        }


    }
}

 

 

폰넘버가 뭐라고 이게 이렇게 어려웠을까.....

ㅠㅠ 역시 게을렀기 때문이라고 한다....ㅎ

 

 

java.lang.IllegalArgumentException: view must not be null




환경 : dialog를 띄워야하는 상황


오류원인 : view가 null 값으로 들어감


해결방안 : view를 명시해준다.


기존코드

  LayoutInflater layoutInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);

  layoutInflater.inflate(R.layout.calling_window_pop, null);

  ButterKnife.bind(this, view);



수정코드


  View view;


  ...


  LayoutInflater layoutInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);

  view = layoutInflater.inflate(R.layout.calling_window_pop, null);

  ButterKnife.bind(this, view);




 

Log.d(TAG, "정다실버")   <-  보통 이런 방식으로 많이 사용함 !

 

Log.e(String, String)   (ERROR)

Log.w(String, String)   (WARNING)

Log.i(String, String)   (INFO)

Log.d(String, String)   (DEBUG)

Log.v(String, String)   (VERBOSE)

Log.wtf(String, String) (ASSERT)

 

이놈의 다이얼로그 생각 보다 정말 많이 쓰이는데..

그냥 기본 설정으로 쓰는 곳이 단 하나도 없다는게...

그래서 어플을 만들때 다이얼로그는 커스텀해서 사용하는 경우가 아마 대부분일거다.

 

그래서 커스텀 다이얼로그를 공부하다가 가장 간단한 방법으로 사용할 수 있는 방법을 포스팅해보려고한다.

다음과 같은 순서로 진행한다.

 

* 커스텀 다이얼로그 만들기

1. 다이얼로그 디자인 -> xml

2. 메인 xml에서 다이얼로그가 나올 수 있는 버튼 만들기

3. 메인 java에서 버튼 클릭 이벤트로 다이얼로그 띄우기

 

간단히 설명하자면 개념은 이러하다.

일반적으로 다이얼로그는 기본 셋팅에 있는대로 속성을 줘서 바로 클릭 이벤트와 같은 곳에 띄우도록 한다.

그런데 커스텀 다이얼로그는 일단 커스텀이기 때문에 보여질 화면을 작업해야하고, 화면을 가지고서 다이얼로그를 띄울 수 있도록 클릭 이벤트에 설정해줘서 띄우도록 해야한다.

그럼 만들어보쟈

 

 

1. 다이얼로그 화면 디자인

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:gravity="center"
        android:orientation="vertical"
        android:layout_marginVertical="50sp"
        android:layout_marginHorizontal="55sp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:text="삭제하시겠습니까?"
            android:textSize="23sp"
            android:gravity="center"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

        <LinearLayout
            android:orientation="horizontal"
            android:layout_marginTop="40sp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <Button
                android:id="@+id/deleteBtn"
                android:text="삭제"
                android:textSize="18sp"
                android:textColor="@color/colorRed"
                android:background="@color/colorNothing"
                android:layout_weight="1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
            <Button
                android:id="@+id/cancelBtn"
                android:text="취소"
                android:textSize="18sp"
                android:textColor="@color/colorAccent"
                android:background="@color/colorNothing"
                android:layout_weight="1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </LinearLayout>

</LinearLayout>

 

2. 메인 화면 디자인

<RelativeLayout
        android:layout_alignParentTop="true"
        android:background="@color/colorGray"
        android:layout_width="match_parent"
        android:layout_height="85sp">
        <ImageView
            android:id="@+id/deleteDataBtn"
            android:src="@drawable/ic_cancel"
            android:layout_alignParentRight="true"
            android:layout_marginRight="20sp"
            android:layout_width="25sp"
            android:layout_height="wrap_content"/>
    </RelativeLayout>

 

3. 메인 자바 코딩

public class CustomDialogEx extends AppCompatActivity {

    ImageView deleteDataBtn;

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

        // 사용할 액티비티 선언
        deleteDataBtn= findViewById(R.id.deleteDataBtn);

        // 삭제하기 버튼 -> 커스텀 다이얼로그 생성
        deleteDataBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DleteAlertDialog();
            }
        });


    }

    private void DleteAlertDialog() {
        LayoutInflater dialog = LayoutInflater.from(this);
        final View deleteDialogLayout = dialog.inflate(R.layout.dialog_delete, null);
        final Dialog deleteDialog = new Dialog(this);

        deleteDialog.setContentView(deleteDialogLayout);
        deleteDialog.show();

        Button deleteBtn = (Button) deleteDialogLayout.findViewById(R.id.deleteBtn);
        Button cancelBtn = (Button) deleteDialogLayout.findViewById(R.id.cancelBtn);

        deleteBtn.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                // 삭제 버튼 누르면 할거
                Toast.makeText(getApplicationContext(), "버튼을 누를시 데이터를 삭제합니다.", Toast.LENGTH_LONG).show();
            }
        });

        cancelBtn.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
            	// 취소 버튼 누르면 할거
                //deleteDialog.cancel();
                //deleteDialog.dismiss();
                Toast.makeText(getApplicationContext(), "취소합니다.", Toast.LENGTH_LONG).show();
            }
        });
    }

}

 

 

위와 같이 하면 된다.

지금 내가 작업중인 파일이라서 클래스명을 좀 바꾸고 필요없는건 빼서

그대로 복붙해서 실행될지는 모르겠지만, 이와 같은 방법으로 진행하면 된다.

 

default 값으로 되어 있는 화면 전환 애니메이션은 너무 구리다ㅋ

엄청 삐까리한건 아마도 API나 오픈 소스를 통해서 찾아봐야할 것 같은데,

가장 깔끔한 페이드인, 페이드아웃 애니메이션을 적용해보려고 한다.

 

1. res 에 anim 라는 폴더를 추가하고, fadein.xml 과 fadeout.xml 파일을 생성한다.

anim 폴더

 

나는 다음과 같은 방법으로 파일을 추가했다.

그리고 파일 명과 함께 확장자를 적어주면 된다.

 

(파일생성과 파일명 적는 방법을 적어놓은 이유는 나같은 완전 초보자는 파일 생성도 힘들기 때문에 적어두었다......)

따라서, 더 좋은? 혹은 간편한 방법을 안다면 사용해도 무방합니다.

 

 

2. 각 파일에 다음과 같은 코드를 구현한다.

<!-- fadein.xml -->

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:fromAlpha="0.0"
    android:toAlpha="1.0" />
<!-- fadeout.xml -->

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:fromAlpha="1.0"
    android:toAlpha="0.0" />

각 파일에 다음고 같이 코딩하면 됩니다. 복붙 궈궈..

duration, fromAlpha, toAlpha 속성을 다음과 같이 지정해주면 됩니다.

각 안드로이드 속성을 확인하면 어떤 의미인지 대충 감이 오지요?-?

 

 

3.  화면 전환 intent 코드에 삽입한다. (intent 코드를 적용하는 java 파일에서 작업합니다.)

Button.setOnClickListener(new View.OnClickListener() {
	@Override
	public void onClick(View v) {
		Intent intent = new Intent(getApplicationContext(), 이동할 페이지의 자바 파일명.class);
		startActivity(intent);
		overridePendingTransition(R.animation.fadein, R.animation.fadeout);
	}
 });

 

다음과 같이 overrigePendingTransition 메서드를 추가해줬습니다.

 

추가적으로, 아무런 애니메이션을 주고 싶지 않다면 다음 코드를 사용하시면 됩니다.

overridePendingTransition(0, 0);

 

 

그럼 최종적으로 아름답게 스르륵- 스르륵- 넘어가는 앱이 될 것 입니다. ㅎ

 

 

예를 들어서 이런 화면이 있다고 하자.

이 화면은 하나의 .xml에 3개의 A,B,C 버튼 액티비티가 있는 예시 그림이다.

 Main.xml 과 각각의 A.xml   B.xml   C.xml 이 있다고 가정한다. 

호기롭게 intent를 사용하여, xml 내부의 버튼을 이용하여

Main - A - Main - B - Main - C -Main 

으로 이동했다.

마지막에 만족스러운 마음으로 하드웨어 버튼으로 어플을 종료하려는 순간

Main - C - Main - B - Main - A -Main 

와 비슷한 순서대로 내가 들어갔다가 나왔던 화면들이 쌓여있어서 하드웨어 버튼을 8번을 눌러야 종료가 되었다.

 

헉... 이럴땐 어떻게 해야하는가?

이건 Manifest 에서 각 .xml의 속성에 다음 포스팅을 참조하여 추가하면 된다.

https://m.blog.naver.com/PostView.nhn?blogId=manhdh&logNo=120162763951&proxyReferer=https:%2F%2Fwww.google.com%2F

 

Activity 의 LaunchMode

LaunchMode 란?엑티비티 실행시 쌓이는 엑티비티 스택에 대한 운영 방식을 설정하는 모드이다.하나의 엑...

blog.naver.com

https://blog.naver.com/huewu/110087045138?proxyReferer=

 

안드로이드 알아두면 요긴한 FLAG_ACTIVITY 네 가지

<안드로이드는 굉장히 유용한 FLAG_ACTIVITY 들을 제공합니다.> 일반적인 경우, 안드로...

blog.naver.com

 

 

 

+ Recent posts