이번 글에서는

앱 개발 프레임워크인 Flutter  Android Studio, Xcode 를 설치하여 Android, iOS 앱 개발 환경을 구축하겠습니다.

 

Android Studio 는 구글이 만든 Android 앱 개발을 위한 공식 개발 도구입니다. Windows, macOS 등 다양한 OS에 설치가 가능합니다.

 

Xcode는 애플이 만든 iOS 앱 개발을 위한 공식 개발 도구입니다. Xcode 는 macOS 에서만 동작하기 때문에 Windows 환경에서는 iOS 앱을 빌드하고 출시하기가 어렵습니다.

 

이런 이유로 이 글은 Android, iOS 모두 출시가 가능한 macOS 환경을 기준으로 진행합니다.

 

 

Flutter 를 사용하는 이유

이 시리즈 글은 Flutter 기반으로 앱 개발을 진행합니다. Flutter는 Google이 만든 앱 개발 프레임워크로 한 번의 코드 작성으로 iOS, Android, 웹, 데스크탑 앱까지 만들 수 있기 때문에 개발 효율이 높습니다.

 

 

macOS 터미널 환경 세팅(생략 가능)

macOS 에서 제공하는 기본 터미널을 사용할 수 있지만, 더 보기 좋고 편하게 이용하기 위해서 터미널 환경을 추가로 설정해줄 수 있습니다.

 

터미널 환경 세팅은 필수가 아니므로 생략 가능하며, 원하는 경우에 한해 아래 링크를 참고하여 진행해주세요.

👉🏼 macOS 터미널 환경 세팅 한 번에 끝내기

 

 

 

1. Homebrew 설치

*이미 Homebrew 를 설치 했다면 이 과정을 생략해주세요.

 

Homebrew 는 Mac용 패키지 관리 도구입니다.

개발에 필요한 다양한 패키지를 설치하기 위해 가장 먼저 설치합니다.

 

개인 Mac의 터미널을 열고 아래 명령어를 입력하여 Homebrew 를 설치합니다.

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

 

 

2. Flutter 설치

먼저 터미널을 열고 아래 brew 명령어를 통해 Flutter 를 설치합니다.

brew install flutter

 

Flutter 를 성공적으로 설치 했다면 doctor 명령어를 통해 현재 Flutter 개발환경 상태를 확인할 수 있습니다.

flutter doctor

 

아직 Android Studio 와 Xcode 를 설치하지 않았기에 관련 부분에 [x] 표시가 되어있을 겁니다.

 

 

3. Android Studio 설치

터미널에서 brew 명령어를 통해 Android Studio 를 설치합니다.

brew install --cask android-studio

 

Android Studio 를 성공적으로 설치 했다면 실행을 해줍니다.

Install Type 에 Standard 옵션을 선택하고 Next 버튼을 눌러줍니다.

 

 

License Agreement 화면에서 android-sdk-license  android-sdk-arm-dbt-license 을 선택하여 모두 Accept 옵션을 선택하고 Finish 버튼을 눌러줍니다.

 

 

Android Studio 설치가 완료되면 아래와 같은 화면이 나타납니다. More Actions 버튼을 누르고 SDK Manager 를 선택해주세요.

 

 

SDK Tools 탭을 선택하고 Android SDK Command-line Tools (latest) 을 체크하고 OK 버튼을 눌러주세요.

 

 

License Agreement 화면에서 Accept 를 선택하고 Next 버튼을 눌러 설치를 진행합니다.

설치가 완료되었다면 터미널로 다시 돌아가 아래 명령어를 입력해줍니다.

flutter doctor --android-licenses

 

 

다시 flutter doctor 명령어를 입력하여 Android toolchain  Android Studio 항목이 [v] 상태로 바뀌었는지 확인합니다.

 

 

4. Xcode 설치

Xcode 를 설치하기 전에 현재 macOS 를 최신 버전으로 업데이트 할 것을 권장합니다. 만약 macOS 와 Xcode 가 구버전일 경우 iOS 앱 빌드가 정상적으로 되지 않을 수 있습니다.

 

App Store 를 열고 Xcode 를 검색하여 다운로드 및 설치합니다. 이때 platform 선택화면에서 iOS 를 선택해야 iOS Simulator를 사용할 수 있습니다.

 

만약 iOS를 선택하지 않고 설치했다면, 추후 Xcode - [Settings] - [Components] 탭에서 추가할 수 있습니다. (아래 이미지 참고)

 

 

Xcode 를 성공적으로 설치했다면 다음으로 아래 명령어를 통해 cocoapods 을 설치하겠습니다.

*cocoapods 은 iOS와 macOS 애플리케이션 개발을 위한 의존성 관리 도구입니다.

brew install cocoapods

 

 

cocoapods 까지 설치를 완료했다면 다시 flutter doctor 명령어를 실행해 Flutter 개발 환경을 최종 점검합니다.

 

 

이처럼 모두 [v] 표시로 확인되면다면 Flutter 개발 환경을 성공적으로 구축한 것입니다.

 

만약 아직 [x] 표시인 항목이 있다면 안내 메시지에 따라 해당 항목을 설치 또는 명령어를 입력하여 문제를 해결할 수 있습니다.

 

출처 : https://daco2020.tistory.com/898

 

Flutter 설치 및 개발 환경 구축하기 for Mac

이번 글에서는앱 개발 프레임워크인 Flutter 와 Android Studio, Xcode 를 설치하여 Android, iOS 앱 개발 환경을 구축하겠습니다. Android Studio 는 구글이 만든 Android 앱 개발을 위한 공식 개발 도구입니다. Win

daco2020.tistory.com

 

'iOS' 카테고리의 다른 글

[iOS] 아이폰 개발 기준 해상도  (0) 2021.11.22
iOS Swift 카메라 사진 음성 권한  (0) 2021.11.22
Posted by 꼬장e
,

가상머신 종료후 

 

Windows PowerShell 실행

 

Set-VMVideo -VMName "매니저에서 VM 이름" -HorizontalResolution 2560 -VerticalResolution 1440 -ResolutionType Single

Posted by 꼬장e
,

 

File -> Configure VM Power Actions 접근 자동실행 하고자 하는 OS 체크

'기타(IT)' 카테고리의 다른 글

인치별 해상도 고민  (0) 2022.01.12
VMware 항상 NumLock 켜기  (0) 2021.04.08
파이어폭스 영상 전체화면 전환시 효과 없애기  (6) 2020.06.19
vmware 최적화  (0) 2018.08.31
vmware 속도 개선  (0) 2017.04.08
Posted by 꼬장e
,

CentOS 7.9에서 yum install 명령어를 실행시 오류 발생 하는건 EOL 상태이기 때문에 발생하는 문제로, 기본 미러 사이트에서 더 이상 패키지를 제공하지 않기 때문입니다

 

조치방법

1. 리포지토리 설정 파일 열기
# vi /etc/yum.repos.d/CentOS-Base.repo

 

2. mirrorlist 주석 처리 및 baseurl 에 주소 변경

[base], [updates], [extras], [centosplus] 등 여러 섹션에서 mirrorlist를 주석 처리하고, baseurl 주소중 mirrorlist.centos.orgvault.centos.org로 변경합니다.

 

[base]
name=CentOS-$releasever - Base
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra
baseurl=http://vault.centos.org/centos/$releasever/os/$basearch/
gpgcheck=1

#released updates
[updates]
name=CentOS-$releasever - Updates
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates&infra=$infra
baseurl=http://vault.centos.org/centos/$releasever/updates/$basearch/
gpgcheck=1

#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras&infra=$infra
baseurl=http://vault.centos.org/centos/$releasever/extras/$basearch/
gpgcheck=1

#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus&infra=$infra
baseurl=http://vault.centos.org/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0

yum install epel-release
명령어를 실행하면 vault.centos.org에서 패키지를 정상적으로 가져와 설치할 수 있습니다.

 

[출처] https://samso.tistory.com/entry/Linux-CentOS7-Yum-리포지토리-설정-변경 [삼소의 삼삼한 메모장:티스토리]

Posted by 꼬장e
,

[CentOS7] yum php7 설치

OS 2024. 4. 10. 14:36


CentOS 7에 yum을 이용해 php를 설치하면 5.4.x 버전이 설치가 된다
PHP 7.x 버전을 설치하는 방법은 아래와 같다.

1. 기존버전 삭제

  [root@localhost ~]# yum remove php-*

2. EPEL 설치

  EPEL(Extra Packages for Enterprise Linux): 최신버전 패키지를 제공하는 저장소

  [root@localhost ~]# yum install epel-release # 최신버전 패키지 저장소
  [root@localhost ~]#  yum install yum-utils     # yum-config-manager 명령어 사용 가능, yum 저장소 관리를 위해 설치

  [root@localhost ~]# yum -y install http://rpms.remirepo.net/enterprise/remi-release-7.rpm


3. 원하는 버전 php 활성화

  [root@localhost ~]# yum-config-manager --enable remi-php74

 

  yum-confog-manager는 yum-utils 를 설치할 때 함께 설치되는 프로그램 중 하나이다

  PHP 7.1 이나 7.2를 설치하고 싶을 경우, remi-php70 대신 remi-php71 or remi-php72를 활성화한다


4. PHP 7설치 필수 모듈을 설치

  [root@localhost ~]# yum -y install php

  [root@localhost ~]# yum install php php-mcrypt php-cli php-gd php-curl php-mysql php-ldap php-zip php-fileinfo

 

5. 기타 php 서비스 설치
  yum -y install php-fpm
  yum -y install  php-cli  php-redis  php-brotli php-intl php-gd php-gmp php-imap php-bcmath php-interbase php-json php-mbstring php-mysqlnd php-odbc php-opcache php-memcached php-tidy php-pdo php-pdo-dblib php-pear php-pgsql php-process php-pecl-apcu php-pecl-geoip php-pecl-imagick php-pecl-hrtime php-pecl-json php-pecl-memcache php-pecl-mongodb php-pecl-rar php-pecl-pq php-pecl-redis4 php-pecl-yaml php-pecl-zip


6. 설치 확인

  [root@localhost ~]# php-v

 

 

 

Posted by 꼬장e
,

참고 : https://velog.io/@plashdof/%EC%95%8C%EB%9E%8C%EA%B6%8C%ED%95%9C-%EB%B6%84%EA%B8%B0%EC%B2%98%EB%A6%AC-Android-13-Android-12

 

targetSdkVersion 33

 

manifest 파일

 

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>

 

<uses-permission android:name="com.google.android.gms.permission.AD_ID"/> // 광고

 

private val requiredPermissionList = if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){
    arrayOf(  // 안드로이드 13 이상 필요한 권한들
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.READ_MEDIA_IMAGES,
        Manifest.permission.READ_MEDIA_VIDEO,
        Manifest.permission.POST_NOTIFICATIONS
    )
}else if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
    arrayOf(  // 안드로이드 13 미만 마시멜로우 이상 필요한 권한들
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE
    )
}

 

 

// 권한 체크 이후로직
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grandResults) {
        // READ_PHONE_STATE의 권한 체크 결과를 불러온다
        if (requestCode == 1001) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
                boolean check_result = true;
                // 모든 퍼미션을 허용했는지 체크
                for (int result : grandResults) {
                    if (result != PackageManager.PERMISSION_GRANTED) {
                        check_result = false;
                        break;
                    }
                }

                // 권한 체크에 동의를 하지 않으면 안드로이드 종료
                if (check_result == true) {
                } else {
                    finish();
                }
            }
        }
    }

Posted by 꼬장e
,

build.gradle 파일에 아래의 옵션을 추가한다.

android {
    lintOptions {
        checkReleaseBuilds false
        // Or, if you prefer, you can continue to check for errors in release builds,
        // but continue the build even when errors are found:
        abortOnError false
    }

Posted by 꼬장e
,

관리자 권한 CMD 에서 

dism.exe /online /cleanup-image /restorehealth 명령어 시도

Posted by 꼬장e
,

출처 : https://snowdeer.github.io/android/2017/03/26/notification-listener-service-sample ( snowdeer's Code Holic )

 

안드로이드 노티피케이션(Notification)의 정보를 가져오는 코드를 작성해보도록 하겠습니다. 안드로이드에서 NotificationListenerService라는 서비스 형태의 컴포넌트를 제공하고 있습니다. API 버전 18부터 사용가능하며, 원할하게 쓰려면 API 버전 19 이상을 추천합니다.

안드로이드 SDK에 기본으로 탑재되어 있기 때문에 gradle 등에 별도로 추가할 필요는 없고, manifest.xml에 다음과 같이 서비스를 등록해주면 됩니다.

 

AndroidManifest.xml

<application
  android:allowBackup="true"
  android:icon="@mipmap/ic_launcher"
  android:label="@string/app_name"
  android:supportsRtl="true"
  android:theme="@style/AppTheme">
  ...
  <service
    android:name=".SnowNotificationListenerService"
    android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
    <intent-filter>
      <action android:name="android.service.notification.NotificationListenerService"/>
    </intent-filter>
  </service>
</application>

 

그리고, Java 코드는 다음과 같습니다.

SnowNotificationListenerService.java

import android.app.Notification;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;

public class SnowNotificationListenerService extends NotificationListenerService {

  @Override
  public void onCreate() {
    super.onCreate();
    Log.i("NotificationListener", "[snowdeer] onCreate()");
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    Log.i("NotificationListener", "[snowdeer] onStartCommand()");
    return super.onStartCommand(intent, flags, startId);
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    Log.i("NotificationListener", "[snowdeer] onDestroy()");
  }

  @Override
  public void onNotificationPosted(StatusBarNotification sbn) {
    Log.i("NotificationListener", "[snowdeer] onNotificationPosted() - " + sbn.toString());
    Log.i("NotificationListener", "[snowdeer] PackageName:" + sbn.getPackageName());
    Log.i("NotificationListener", "[snowdeer] PostTime:" + sbn.getPostTime());

    Notification notificatin = sbn.getNotification();
    Bundle extras = notificatin.extras;
    String title = extras.getString(Notification.EXTRA_TITLE);
    int smallIconRes = extras.getInt(Notification.EXTRA_SMALL_ICON);
    Bitmap largeIcon = ((Bitmap) extras.getParcelable(Notification.EXTRA_LARGE_ICON));
    CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
    CharSequence subText = extras.getCharSequence(Notification.EXTRA_SUB_TEXT);

    Log.i("NotificationListener", "[snowdeer] Title:" + title);
    Log.i("NotificationListener", "[snowdeer] Text:" + text);
    Log.i("NotificationListener", "[snowdeer] Sub Text:" + subText);
  }

  @Override
  public void onNotificationRemoved(StatusBarNotification sbn) {
    Log.i("NotificationListener", "[snowdeer] onNotificationRemoved() - " + sbn.toString());
  }

}

 

참고로 이 서비스는 별도로 startService를 해주지 않아도, 사용자의 권한만 주어지면 자동으로 시작되기 때문에 startService를 구현할 필요는 없습니다.

사용자의 권한을 요청하는 코드는 다음과 같습니다.

Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
startActivity(intent);

 

하지만, 위 코드를 매번 호출하는 것은 사용자에게 상당히 번거로운 일이기 때문에, 다음과 같은 코드를 이용해서 기존에 사용자가 해당 App에 권한을 부여한 적이 있는지 확인하는 것이 좋습니다.

 

MainActivity.java

import android.content.Intent;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.util.Set;

public class MainActivity extends AppCompatActivity {

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

    boolean isPermissionAllowed = isNotiPermissionAllowed();

    if(!isPermissionAllowed) {
      Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
      startActivity(intent);
    }
  }

  private boolean isNotiPermissionAllowed() {
    Set<String> notiListenerSet = NotificationManagerCompat.getEnabledListenerPackages(this);
    String myPackageName = getPackageName();

    for(String packageName : notiListenerSet) {
      if(packageName == null) {
        continue;
      }
      if(packageName.equals(myPackageName)) {
        return true;
      }
    }

    return false;
  }
}
Posted by 꼬장e
,

출처 : http://it-archives.com/222114870620/  (흑곰의 유익한 블로그 2호점)

아이폰(iOS) 하이브리드 앱에서는 <input type=”file”> 을 사용하면 해당 요소를 클릭 시 파일선택 창(FileChooser)이 잘 뜨지만, 안드로이드 웹뷰에서는 <input type=”file”> 요소를 클릭해도 아무 반응이 없다.

안드로이드 하이브리드앱에서 파일을 선택하는 기능은 아래와 같이 구현 가능하다.

1. mFilePathCallback 변수를 전역변수로 선언한다.

webView 를 사용할 Activity 클래스(또는 Activity 를 상속받은 클래스) 에 선언하면 된다.

    // 안드로이드 웹뷰에서 파일 첨부하기
    ValueCallback mFilePathCallback = null;

2. WebChromeClient 클래스의 onShowFileChooser 메서드를 오버라이딩해서 아래와 같이 작성한다.

파일 n개 선택(파일 여러개 선택, 파일 다중선택) 하려면 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); 코드를 포함시키자.

    webView.setWebChromeClient(new WebChromeClient() {
        // 안드로이드 웹뷰에서 파일 첨부하기
        @Override
        public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) {
            mFilePathCallback = filePathCallback;

            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            intent.setType(“image/*”);

            // 파일 n개 선택 가능하도록 처리
            intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);

            startActivityForResult(intent, 0);
            return true;
        }
    });

만약 WebChromeClient 를 상속받아 별개의 자바파일로 구현했다면(ex : public class OOOWebChromeClient extends WebChromeClient {}), startActivityForResult 메서드를 곧바로 사용할 수 없을 것이다.

이 경우 WebChromeClient 생성자에서 activity 를 받아 thisActivity 라는 멤버변수에 저장해놓으면 thisActivity.startActivityForResult(intent, 0); 식으로 사용할 수 있다.

public class OOOWebChromeClient extends WebChromeClient {
    private final Activity thisActivity;

    public OOOWebChromeClient(Activity thisActivity) {
        super();
        this.thisActivity = thisActivity;
    }

    (중략)

}   

3. mFilePathCallback 변수가 선언된 클래스, 다시 말해서 Activify 클래스(또는 Activity를 상속한 클래스) 하단에 아래와 같이 작성한다.

참고로 파일을 n개 선택(여러개 선택, 다중선택)한 경우 data.getClipData() 값이 not null이며, 파일을 1개 선택한 경우 data.getData() 값이 not null이다.

    // 안드로이드 웹뷰에서 파일 첨부하기
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        Log.e(“resultCode:: “, String.valueOf(resultCode));
        if (requestCode == 0 && resultCode == Activity.RESULT_OK) {

            // 파일 n개 선택한 경우
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && data != null && data.getClipData() != null && data.getClipData().getItemCount() > 0) {
                int count = data.getClipData().getItemCount();

                Uri[] uriArr = new Uri[count];
                for (int i=0; i<count; i++) {
                    uriArr[i] = data.getClipData().getItemAt(i).getUri();
                }

                mFilePathCallback.onReceiveValue(uriArr);

            } else if (data != null && data.getData() != null) {
                // 파일 1개 선택한 경우
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    mFilePathCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
                } else {
                    mFilePathCallback.onReceiveValue(new Uri[]{data.getData()});
                }
            }

            mFilePathCallback = null;

        } else {
            mFilePathCallback.onReceiveValue(null);
        }
    }

만약 WebChromeClient 를 상속받아 별개의 자바파일로 구현했다면(ex : public class OOOWebChromeClient extends WebChromeClient {}), mFilePathCallback 변수를 사용할 수 없을 것이다. OOOWebChromeClien 클래스 내에 mFilePathCallback 의 getter, setter 메서드를 만들어서 접근하도록 하자.

Posted by 꼬장e
,