[Android] APNG 사용하기

프로그래밍/ANDROID|2023. 8. 24. 08:10
반응형

움직이는 GIF 파일과 같이 움직이는 PNG 파일인 APNG 입니다.

GIF 보다 용량도 작고 고화질로 출력이 가능해 안드로이드의 아이콘이나 버튼으로 사용하기 좋습니다.

 

라이브러리 :  https://github.com/penfeizhou/APNG4Android

 

1. 준비

build.gradle 파일에 dependency 를 추가합니다.

...

repositories {
    mavenCentral()
}


...

APNG
dependencies {
    implementation 'com.github.penfeizhou.android.animation:apng:${VERSION}'
}


...

 

2. 사용

APNG 파일 (예: sysdocu.png) 은 assets 폴더에 저장합니다.

...

// asset 파일에서 불러오기
AssetStreamLoader assetLoader = new AssetStreamLoader(context, "sysdocu.png");

// APNG Drawable 생성
APNGDrawable apngDrawable = new APNGDrawable(assetLoader);

// 자동 실행
imageView.setImageDrawable(apngDrawable);

...

 

반응형

댓글()

FCM 을 활용한 PUSH 메세지 보내기 (2022-10-19)

프로그래밍/ANDROID|2022. 10. 19. 16:16
반응형

New Project > Empty Activity 선택

Language : Java

Minimum SDK : API 21

[v] Use legacy android.support libraries

이 상태로 진행하였습니다.

 

 

build.gradle 수정 (프로젝트 수준)

 

아래 내용을 파일 내용 맨 윗부분에 추가 합니다.

 

buildscript {
    repositories {
        google()  // Google's Maven repository
        mavenCentral()  // Maven Central repository

    }
    dependencies {
        classpath 'com.google.gms:google-services:4.3.14'
    }
}

(생략)

 

 

build.gradle 수정 (앱 수준)

 

아래 내용을 추가 합니다.

중간에 주석 부분도 추가해줍니다. 없을 경우 바로 아랫줄에 구문 에러 메세지가 출력됩니다.

 

plugins {
    id 'com.android.application'
    id 'com.google.gms.google-services' // 이 부분을 추가 합니다.
}

(생략)

dependencies {

    //noinspection GradleCompatible
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:2.0.4'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

    implementation platform('com.google.firebase:firebase-bom:31.0.0')
    implementation 'com.google.firebase:firebase-analytics'
    implementation 'com.google.firebase:firebase-messaging:23.1.0'
    implementation 'com.google.firebase:firebase-core:21.1.1'
}

apply plugin: 'com.google.gms.google-services'

 

 

settings.gradle 생성 (앱 수준)

 

allprojects {
    repositories {
        google()  // Google's Maven repository
        mavenCentral()  // Maven Central repository
    }
}

 

 

google-services.json 생성

 

Firebase console 페이지에 접근해서 프로젝트에 APP 을 추가합니다.

추가하는 과정에서 google-services.json 파일을 다운로드 받을 수 있으며,

해당 파일은 app 디렉토리 안에 넣어 놓습니다.

 

 

build.gradle 페이지에서 [Sync Now] 버튼을 눌러줍니다.

 

 

MyFirebaseMessagingService.java 생성

 

package com.tistory.sysdocu;

import androidx.annotation.NonNull;

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

public class MyFirebaseMessagingService extends FirebaseMessagingService {

    @Override
    public void onNewToken(@NonNull String token) {
        super.onNewToken(token);
        //token을 서버로 전송
    }

    @Override
    public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);
        //수신한 메시지를 처리
    }
}

 

 

MainActivity.java 수정

 

package com.tistory.sysdocu;

import android.os.Bundle;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.messaging.FirebaseMessaging;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;

public class MainActivity extends AppCompatActivity {

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

        FirebaseMessaging.getInstance().getToken().addOnSuccessListener(new OnSuccessListener<String>() {
            @Override
            public void onSuccess(String token) {

                // 토큰을 토스트로 간단히 확인
                Toast toast = Toast.makeText(getApplicationContext(), "TOKEN : " + token, Toast.LENGTH_LONG);
                toast.show();

// 아래와 같이 특정 서버로 데이터를 전송 할 수 있습니다. 
// 서버로 token 을 전송하는 방식은 여러가지가 있으나, 여기에서는 간단히 GET 방식으로 접근하여 전송하도록 하였습니다.
// 웹서버에서는 token 을 DB 에 저장하여 활용하면 됩니다.
/*
                new Thread(){
                    @Override
                    public void run() {
                        StringBuilder content = new StringBuilder();
                        try {
                            URL url = new URL("https://sysdocu.tistory.com/register.html?token=" + token);
                            BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
                            String str;
                            while ((str = in.readLine()) != null) {
                                content.append(str +"\n");
                            }
                            in.close();
                        } catch (MalformedURLException e) {
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }.start();
*/
            }
        });
    }

}

 

 

activity_main.xml 수정

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:background="#DFE5E5"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textColor="@color/black"
        android:text="토큰은 웹서버에서 확인하세요." />

</LinearLayout>

 

 

AndroidManifest.xml

 

아래 내용을 추가합니다.

 

(생략)

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


(생략)

<service
    android:name=".MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT"/>
    </intent-filter>
</service>

(생략)

 

 

프로젝트를 빌드하여 apk 파일을 생성하고, 핸드폰에 설치합니다.

 

 

PUSH 발송 테스트

 

방법1)

Firebase console 페이지에 접근해서 [ 참여 > Messaging ] 메뉴로 들어갑니다.

첫번째 메세지 보내기(?) 에서 [Firebase 알림 메시지] 를 선택하고 앱을 설치한 모든 device 로 PUSH 메세지를 발송해 볼 수 있습니다.

 

 

방법2)

PHP 코드를 아래와 같이 작성 합니다.

 

<?php

$title = "제목입니다.";
$body = "내용입니다.";

$_url = 'https://fcm.googleapis.com/fcm/send';

$_header = array(
    'Content-Type: application/json',
    'Authorization: key=서버 키' // Firebase console > 프로젝트 설정 > 클라우드 메시징에서 '서버 키' 를 복사하여 입력합니다.
);

$_data = array(
    'to' => '사용자 토큰', // 앱에서 출력된 (서버로 전송한) 토큰을 입력합니다.
    'notification' => array( // 앱이 백그라운드 또는 실행중이지 않을때 제목과 내용
        'title' => $title,
        'body' => $body
    ),
    'data' => array( // 앱이 실행중일때 제목과 내용 (내용을 보려면 앱 소스에서 코드를 추가해주세요)
        'title' => $title,
        'body' => $body
    )
);
$_param = json_encode($_data);

$curlObj = curl_init();
curl_setopt($curlObj, CURLOPT_URL, $_url );
curl_setopt($curlObj, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curlObj, CURLOPT_SSLVERSION, 1);
curl_setopt($curlObj, CURLOPT_POST, true);
curl_setopt($curlObj, CURLOPT_HEADER, false);
curl_setopt($curlObj, CURLOPT_HTTPHEADER, $_header);
curl_setopt($curlObj, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curlObj, CURLOPT_POSTFIELDS, $_param);
$response = curl_exec($curlObj);

$_json = array();
$_json = json_decode($response, true);
curl_close($curlObj);
?>

 

소스는 이와 같으며 PHP 파일을 실행하여 token 으로 지정한 특정 device 로 PUSH 메세지를 발송할 수 있습니다.

 

반응형

댓글()

안드로이드 알람 생성 2가지 방법 (Android Notifications Tutorial with Examples)

프로그래밍/ANDROID|2022. 7. 18. 12:13
반응형

아래 예제는 Notification 두 가지 방법을 다루고 있습니다.

1. 진동 및 알림 메세지가 전면 상단에 출력되며, 상태바에서도 아이콘으로 나타나는 방법

2. 진동 및 알림 메세지 없이 맨 위 상태바에서만 조용히 아이콘으로 나타나는 방법

 

 

아래는 원문 출처를 따라하며 요약한 내용입니다.

[출처] https://o7planning.org/10427/android-notification

==================================================

 

 

1. Empty Activity 로 프로젝트를 생성합니다.

 

 

2. 알림 메세지에 사용할 이미지 파일 두개를 생성합니다.

drawable/icon_notify1.png

drawable/icon_notify1.png

 

 

3. 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" >

    <EditText
        android:id="@+id/editText_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:ems="10"
        android:hint="Title"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/editText_message"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:ems="10"
        android:hint="Message"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editText_title" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="33dp"
        android:text="Send on Channel 1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editText_message" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="23dp"
        android:text="Send On Channel 2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button1" />
</androidx.constraintlayout.widget.ConstraintLayout>

 

4. NotificationApp.java

package org.o7planning.notificationbasicexample;

import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;

public class NotificationApp extends Application  {

    public static final  String CHANNEL_1_ID = "channel1";
    public static final  String CHANNEL_2_ID = "channel2";

    @Override
    public void onCreate() {
        super.onCreate();

        this.createNotificationChannels();
    }

    private void createNotificationChannels()  {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel1 = new NotificationChannel(
                    CHANNEL_1_ID,
                    "Channel 1",
                    NotificationManager.IMPORTANCE_HIGH
            );
            channel1.setDescription("This is channel 1");

            NotificationChannel channel2 = new NotificationChannel(
                    CHANNEL_2_ID,
                    "Channel 2",
                    NotificationManager.IMPORTANCE_LOW
            );
            channel1.setDescription("This is channel 2");


            NotificationManager manager = this.getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel1);
            manager.createNotificationChannel(channel2);
        }
    }
}

 

5. AndroidManifest.xml

<application
        android:name=".NotificationApp"
        ....
>
...
</application>

 

6. MainActivity.java

package org.o7planning.notificationbasicexample;

import android.app.Notification;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;


public class MainActivity extends AppCompatActivity {

    private NotificationManagerCompat notificationManagerCompat;

    private EditText editTextTitle;
    private EditText editTextMessage;

    private Button button1;
    private Button button2;


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        this.editTextTitle = (EditText) this.findViewById(R.id.editText_title);
        this.editTextMessage = (EditText) this.findViewById(R.id.editText_message);

        this.button1 = (Button) this.findViewById(R.id.button1);
        this.button2 = (Button) this.findViewById(R.id.button2);

        this.button1.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                sendOnChannel1(  );
            }
        });

        this.button2.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                sendOnChannel2(  );
            }
        });

        //
        this.notificationManagerCompat = NotificationManagerCompat.from(this);
    }


    private void sendOnChannel1()  {
        String title = this.editTextTitle.getText().toString();
        String message = this.editTextMessage.getText().toString();

        Notification notification = new NotificationCompat.Builder(this, NotificationApp.CHANNEL_1_ID)
                .setSmallIcon(R.drawable.icon_notify1)
                .setContentTitle(title)
                .setContentText(message)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setCategory(NotificationCompat.CATEGORY_MESSAGE)
                .build();

        int notificationId = 1;
        this.notificationManagerCompat.notify(notificationId, notification);
    }

    private void sendOnChannel2()  {
        String title = this.editTextTitle.getText().toString();
        String message = this.editTextMessage.getText().toString();

        Notification notification = new NotificationCompat.Builder(this, NotificationApp.CHANNEL_2_ID)
                .setSmallIcon(R.drawable.icon_notify2)
                .setContentTitle(title)
                .setContentText(message)
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .setCategory(NotificationCompat.CATEGORY_PROMO) // Promotion.
                .build();

        int notificationId = 2;
        this.notificationManagerCompat.notify(notificationId, notification);
    }
}

 

빌딩 이후 apk 파일을 설치, 어플을 실행하면 두 가지 예제의 알림 방식을 구분할 수 있습니다.

 

[출처] https://o7planning.org/10427/android-notification

반응형

댓글()

안드로이드 비정상 종료 감지 처리 (어플 재시작)

프로그래밍/ANDROID|2021. 6. 8. 10:18
반응형

아래 내용을 소스에 추가합니다.

(MainActivity 에서 감지시 초기 화면 SplashActivity 로 이동)

 

 

첫번재 방법

 

        // 비정상 종료시 어플 재시작
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, Throwable ex) {
            Intent crashedIntent = new Intent(MainActivity.thisSplashActivity.class);

                Toast.makeText(MainActivity.this, "비정상 종료 되어 재시작 합니다.", Toast.LENGTH_SHORT).show();

                //crashedIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                crashedIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(crashedIntent);
                System.exit(0);
            }
        });

 

 

두번째 방법

 

링크 참조

https://ssaurel.medium.com/how-to-auto-restart-an-android-application-after-a-crash-or-a-force-close-error-1a361677c0ce

 

반응형

댓글()

안드로이드 와이파이 SSID 가져오기

프로그래밍/ANDROID|2021. 6. 2. 08:52
반응형

Android 8 에서 동작하던 SSID 가져오기가 Android 11 에서 안되는 현상이 확인되었습니다.

구글링 해보니 runtime permission 적용하면 된다고 하여 아래소스를 찾아 적용하였습니다.

(기능 사용시 권한 부여)

 


public String SSID = null;


tryToReadSSID(); // 호출하여 아래 내용 실행


    // SSID 가져오기
    private void tryToReadSSID() {
        // If requested permission isn't Granted yet
        if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // Request permission from user
            ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 101);
        } else { // Permission already granted
            WifiManager wifiManager = (WifiManager) getActivity().getApplicationContext().getSystemService(WIFI_SERVICE);
            WifiInfo wifiInfo = wifiManager.getConnectionInfo();
            if(wifiInfo.getSupplicantState() == SupplicantState.COMPLETED) {
                SSID = wifiInfo.getSSID().replaceAll("\"", ""); // SSID 앞뒤에 있는 따옴표 제거하고 SSID 변수에 입력
            }
        }
    }
 

 

반응형

댓글()

TextView 레이아웃 사이즈에 맞게 글자 크기 자동 조정하기

프로그래밍/ANDROID|2021. 6. 2. 07:58
반응형

레이아웃에서 아래와 같이 설정시

기본폰트 18sp 지만, 글자가 많아 레이아웃을 벗어날 경우 2sp 단위로 12sp 까지 줄일 수 있습니다.

 

<TextView
                android:id="@+id/tv_private"
                android:layout_width="0dp"
                android:layout_height="30dp"
                android:layout_weight="4"
                android:layout_marginLeft="20dp"
                android:text="0.0.0.0"
                android:textColor="@color/white"
                android:textSize="18sp" // 기본 크기
                android:textStyle="bold"
                android:gravity="center_vertical"

                android:autoSizeTextType="uniform"
                android:autoSizeMinTextSize="12sp" // 최소 크기
                android:autoSizeMaxTextSize="18sp" // 최대 크기
                android:autoSizeStepGranularity="2sp" /> // 조정 간격

 

반응형

댓글()

안드로이드에서 모바일 네트워크 구분하기 (2G, 3G, 4G, 5G)

프로그래밍/ANDROID|2021. 6. 2. 07:50
반응형

example.java

package com.example.mymobilenetworktype;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.widget.TextView;

import static android.telephony.TelephonyManager.NETWORK_TYPE_1xRTT;
import static android.telephony.TelephonyManager.NETWORK_TYPE_CDMA;
import static android.telephony.TelephonyManager.NETWORK_TYPE_EDGE;
import static android.telephony.TelephonyManager.NETWORK_TYPE_EVDO_0;
import static android.telephony.TelephonyManager.NETWORK_TYPE_EVDO_A;
import static android.telephony.TelephonyManager.NETWORK_TYPE_EVDO_B;
import static android.telephony.TelephonyManager.NETWORK_TYPE_GPRS;
import static android.telephony.TelephonyManager.NETWORK_TYPE_HSDPA;
import static android.telephony.TelephonyManager.NETWORK_TYPE_HSPA;
import static android.telephony.TelephonyManager.NETWORK_TYPE_HSPAP;
import static android.telephony.TelephonyManager.NETWORK_TYPE_IDEN;
import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE;
import static android.telephony.TelephonyManager.NETWORK_TYPE_NR;
import static android.telephony.TelephonyManager.NETWORK_TYPE_UMTS;

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private TelephonyManager telephonyManager;

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

        textView = findViewById(R.id.textView);
        ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_PHONE_STATE}, PackageManager.PERMISSION_GRANTED);
        telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }

        switch (telephonyManager.getDataNetworkType()) {
            case NETWORK_TYPE_EDGE:
            case NETWORK_TYPE_GPRS:
            case NETWORK_TYPE_CDMA:
            case NETWORK_TYPE_IDEN:
            case NETWORK_TYPE_1xRTT:
                textView.setText("2G");
                break;
            case NETWORK_TYPE_UMTS:
            case NETWORK_TYPE_HSDPA:
            case NETWORK_TYPE_HSPA:
            case NETWORK_TYPE_HSPAP:
            case NETWORK_TYPE_EVDO_0:
            case NETWORK_TYPE_EVDO_A:
            case NETWORK_TYPE_EVDO_B:
                textView.setText("3G");
                break;
            case NETWORK_TYPE_LTE:
                textView.setText("4G");
                break;
            case NETWORK_TYPE_NR:
                textView.setText("5G");
                break;
            default:
                textView.setText("Unknown");
        }
    }
}

 

[출처] https://programmerworld.co/android/how-to-detect-the-mobile-network-type-2g-3g-4g-5g-from-your-android-app-android-studio-complete-source-code/

반응형

댓글()

Ubuntu 18.04 에서 Android Studio 및 AVD 설치하기

프로그래밍/ANDROID|2021. 5. 21. 11:18
반응형

Ubuntu 18.04 에서 안드로이드 개발 환경 Android Studio 를 설치하고, AVD 를 이용하여 여러 단말기의 사이즈, OS 종류에서 테스트 할 수 있습니다.

 

1. 설치

# add-apt-repository ppa:maarten-fonville/android-studio

# apt -y update

# apt -y install android-studio

 

2. 실행

# /opt/android-studio/bin/studio.sh

설치 마법사에서 Next 이나 Custom 을 누르면 되고, 설치과정중 AVD 를 선택 후 Finish 를 눌러 설치를 진행합니다.

설치가 완료되어 Android Studio 가 실행되면 기본 프로젝트를 생성하거나 기존 프로젝트를 불러와 사용이 가능합니다.

 

3. AVD 사용

메뉴 Tools > AVD Manager 를 실행합니다.

하단의 [+Create Virtual Device] 버튼을 누르고 원하는 단말기 사이즈와 OS 버전을 선택 후 생성한 뒤,

우측의 아래 화살표 (▼) 를 눌러 Cold Boot Now 를 눌러 단말기 전원을 켜면 가동됩니다.

부팅이 완료되기 까지 시간이 조금 소요됩니다.

 

 

반응형

댓글()

Ubuntu 18.04 내에서 안드로이드 (Android) 에뮬레이터 구동하기

프로그래밍/ANDROID|2021. 4. 15. 09:27
반응형

에뮬레이터 공식 홈페이지 : https://anbox.io/

 

Android 테스트를 위해 리눅스에 Androud Emulator 를 설치하였습니다. (현재 Android 버전 : 7.1.1)

아래는 Ubuntu 18.04 에서 snap 을 이용하여 Anbox 를 설치하는 방법 입니다.

 


 1. 설치

Anbox Support PPA 를 등록합니다.

 

# add-apt-repository ppa:morphis/anbox-support
# apt -y update
# apt -y install linux-headers-generic anbox-modules-dkms
# modprobe ashmem_linux
# modprobe binder_linux

snap 을 이용해 Anbox 를 설치 합니다.

 

# apt -y install snap snapd

# snap install --devmode --beta anbox

 

우분투 '프로그램 표시' 에서 설치된 'Anbox Application manager' 를 볼 수 있습니다.

하지만 Google Play Store 가 보이지 않습니다.

아래 작업을 추가로 진행하면 아이콘이 추가되고 사용이 가능합니다.

 

 

2. 추가 작업 (Google Play Store)

 

# apt -y install curl wget lzip unzip squashfs-tools

# wget https://raw.githubusercontent.com/geeks-r-us/anbox-playstore-installer/master/install-playstore.sh

# bash install-playstore.sh

 

다시 'Anbox Application manager' 를 실행하면 Google Play Store 아이콘이 보이게 됩니다.

 

 

3. apk 파일 설치

apk 패키지 파일을 설치하는 방법은 아래와 같습니다.

# apt install android-tools-adb
# adb install my.apk    // 미리 다운로드 받은 파일을 설치합니다.

 

혹시 아래와 같은 에러를 만난 경우의 조치법입니다. -t 옵션을 추가하면 간단히 해결 됩니다.

# adb install my.apk

adb: failed to install my.apk: Failure [INSTALL_FAILED_TEST_ONLY: installPackageLI]

 

# adb install -t my.apk

Success

 

다시 'Anbox Application manager' 를 실행하면 설치했던 앱이 보입니다.

 

반응형

댓글()

리눅스 CentOS, Ubuntu 에서 Android-x86 설치하기

프로그래밍/ANDROID|2021. 3. 25. 09:36
반응형

Android-x86 은 가상 머신처럼 별도의 창에서 Android 를 사용하는것이 아니고,

PC 부팅시 커널 선택 화면에서 Android-x86 을 선택하면 Android 환경으로 부팅이 됩니다.

 

1. 다운로드

 

아래 주소에서 원하는 Android 버전을 다운로드 합니다.

CentOS 에서는 .rpm 파일을 받으면 되지만

Ubuntu 에서는 .deb 파일이 보이지 않으므로 그냥 .rpm 파일을 받습니다.

 

https://www.android-x86.org/

 

2. 설치

 

1) CentOS

# rpm -ivh android-x86-9.0-r2.x86_64.rpm

 

2) Ubuntu

# apt -y install alien

# alien -ci android-x86-9.0-r2.x86_64.rpm

 

3. 적용

 

# reboot

부팅시 Android-x86 선택

 

 

반응형

댓글()

애드몹 광고가 출력되지 않는 경우

프로그래밍/ANDROID|2021. 3. 18. 09:19
반응형

애드몹 광고가 안나오는 경우

 

아래는 경험을 바탕으로 작성되었으며, 다른 경우가 존재할 수 있으므로 참고로만 살펴보세요.

 

1) 구글플레이에 개발자로 등록하고 어플 등록 (개시하지 않더라도) 하면 나올것임

 

2) 구글 광고 테스트ID 가 아니고 발급받은 광고ID 로 테스트를 계속할 경우

    처음에는 잘 나오다가 광고가 소진되어 화면이 하얗게만 나올 수 있음

    이때는 app-release.apk 로 빌드하고 설치해보면 잘 나옴

 

* 에러 증상을 확인하는 코드

 

        // mAdView.loadAd(adRequest); 로 광고를 불러올 경우

        mAdView.setAdListener( new AdListener() {

            @Override

            public void onAdLoaded() {

                super.onAdLoaded();

                Toast.makeText(getApplicationContext(), "광고 출력 되었습니다.", Toast.LENGTH_SHORT ).show();

            }

            @Override

            public void onAdFailedToLoad(int i) {

                super.onAdFailedToLoad(i);

                String errorMsg = "에러 코드 : (" + i + ")";

                Toast.makeText(getApplicationContext(), errorMsg, Toast.LENGTH_SHORT ).show();

            }

        });

 

 

 

 

반응형

댓글()