안드로이드 5기 2017년 강의 정리 6 (오준석의 생존코딩)
https://www.youtube.com/watch?v=InvEpfiD3P0&list=PLxTmPHxRH3VWSF7kMcsIaTglWUJZpWeQ9&index=125
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki
MyFirstAndroidApp5ki-master.zip
43일차 Palette
43일차 Firebase
46일차 Gradle 이것저것
47일차 설정화면
48일차 소셜로그인
48일차 ChromeCustomTab, BottomNavigationView
48일차 TransitionDrawable, Animation
49일차 조도 센서
50일차 TedPermission 권한 체크
51일차 Firebase Functions
52일차 빌드 자동화 gradle
43일차 Palette
# 누구나 쉽게 이해할 수 있는 Git 에 입문 (backlog.com)
https://backlog.com/git-tutorial/kr/intro/intro1_1.html
# Selecting Colors with the Palette API (android developers)
https://developer.android.com/training/material/palette-colors
public class PaletteActivity extends AppCompatActivity {
private ActivityPaletteBinding mBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this,
R.layout.activity_palette);
BitmapFactory.Options options = new BitmapFactory.Options();
// 비트맵 샘플링 (용량줄이기) 2의 배수
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.girlfriend, options);
createPaletteAsync(bitmap);
}
public void createPaletteAsync(Bitmap bitmap) {
Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
public void onGenerated(Palette p) {
// Use generated instance
Palette.Swatch vibrantSwatch = p.getVibrantSwatch();
if (vibrantSwatch != null) {
mBinding.layout1.setBackgroundColor(vibrantSwatch.getRgb());
mBinding.title1.setTextColor(vibrantSwatch.getTitleTextColor());
mBinding.content1.setTextColor(vibrantSwatch.getBodyTextColor());
}
Palette.Swatch darkVibrantSwatch = p.getDarkVibrantSwatch();
if (darkVibrantSwatch != null) {
mBinding.layout2.setBackgroundColor(darkVibrantSwatch.getRgb());
mBinding.title2.setTextColor(darkVibrantSwatch.getTitleTextColor());
mBinding.content2.setTextColor(darkVibrantSwatch.getBodyTextColor());
}
Palette.Swatch lightVibrantSwatch = p.getLightVibrantSwatch();
if (lightVibrantSwatch != null) {
mBinding.layout3.setBackgroundColor(lightVibrantSwatch.getRgb());
mBinding.title3.setTextColor(lightVibrantSwatch.getTitleTextColor());
mBinding.content3.setTextColor(lightVibrantSwatch.getBodyTextColor());
}
Palette.Swatch mutedSwatch = p.getMutedSwatch();
if (mutedSwatch != null) {
mBinding.layout4.setBackgroundColor(mutedSwatch.getRgb());
mBinding.title4.setTextColor(mutedSwatch.getTitleTextColor());
mBinding.content4.setTextColor(mutedSwatch.getBodyTextColor());
}
Palette.Swatch darkMutedSwatch = p.getDarkMutedSwatch();
if (darkMutedSwatch != null) {
mBinding.layout5.setBackgroundColor(darkMutedSwatch.getRgb());
mBinding.title5.setTextColor(darkMutedSwatch.getTitleTextColor());
mBinding.content5.setTextColor(darkMutedSwatch.getBodyTextColor());
}
Palette.Swatch lightMutedSwatch = p.getLightMutedSwatch();
if (lightMutedSwatch != null) {
mBinding.layout6.setBackgroundColor(lightMutedSwatch.getRgb());
mBinding.title6.setTextColor(lightMutedSwatch.getTitleTextColor());
mBinding.content6.setTextColor(lightMutedSwatch.getBodyTextColor());
}
Palette.Swatch dominantSwatch = p.getDominantSwatch();
if (dominantSwatch != null) {
mBinding.layout7.setBackgroundColor(dominantSwatch.getRgb());
mBinding.title7.setTextColor(dominantSwatch.getTitleTextColor());
mBinding.content7.setTextColor(dominantSwatch.getBodyTextColor());
}
}
});
}
}
# activity_palette.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout 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"
android:orientation="vertical"
tools:context="com.example.myapplication.activities.PaletteActivity">
<ImageView
android:id="@+id/imageView3"
android:layout_width="match_parent"
android:layout_height="150dp"
app:srcCompat="@drawable/girlfriend" />
<LinearLayout
android:id="@+id/layout_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/title_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="24sp" />
<TextView
android:id="@+id/content_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/title_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="24sp" />
<TextView
android:id="@+id/content_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/title_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="24sp" />
<TextView
android:id="@+id/content_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/title_4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="24sp" />
<TextView
android:id="@+id/content_4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/title_5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="24sp" />
<TextView
android:id="@+id/content_5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/title_6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="24sp" />
<TextView
android:id="@+id/content_6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_7"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/title_7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="24sp" />
<TextView
android:id="@+id/content_7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecting Colors with the Palette API"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
</layout>
43일차 Firebase
# Google Codelabs
https://codelabs.developers.google.com/
# Firebase Android Codelab – Build Friendly Chat
https://codelabs.developers.google.com/codelabs/firebase-android?hl=en
46일차 Gradle 이것저것
# 메소드 개수 제한 해제 하려면 multidex 적용할 것
# 앱을 출시할 때는 난독화를 할 것
minifyEnabled true // 잘되면 그냥 출시, 안되면 설정 수정
# proguard-rules.pro // 난독화 룰에 대한 파일
47일차 설정화면
# Settings (android developers)
https://developer.android.com/guide/topics/ui/settings#java
# Settings Activity 추가
48일차 소셜로그인
# Android용 Facebook 로그인 – 빠른 시작
https://developers.facebook.com/docs/facebook-login/android
48일차 ChromeCustomTab, BottomNavigationView
# Chrome Custom Tabs (chrome developer)
https://developer.chrome.com/multidevice/android/customtabs
# BottomNavigationView (android developer)
https://developer.android.com/reference/com/google/android/material/bottomnavigation/BottomNavigationView
48일차 TransitionDrawable, Animation
# Drawable resources (android developers)
https://developer.android.com/guide/topics/resources/drawable-resource
# Animation resources (android developers)
https://developer.android.com/guide/topics/resources/animation-resource
# Android scale animation on view
https://stackoverflow.com/questions/7414065/android-scale-animation-on-view
# AnimationActivity.java
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/java/com/example/myapplication/activities/AnimationActivity.java
# activity_animation.xml
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/res/layout/activity_animation.xml
# transition.xml
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/res/drawable/transition.xml
# scale.xml
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/res/anim/scale.xml
# shake.xml
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/res/anim/shake.xml
translate.xml
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/res/anim/translate.xml
public class AnimationActivity extends AppCompatActivity {
ImageView mImageView;
EditText mEditText;
Animation mShakeAnimation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animation);
mShakeAnimation = AnimationUtils.loadAnimation(this, R.anim.shake);
mShakeAnimation.setInterpolator(new CycleInterpolator(7));
mImageView = (ImageView) findViewById(R.id.image_view);
mEditText = (EditText) findViewById(R.id.edit_text);
mEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
mEditText.startAnimation(mShakeAnimation);
}
});
}
public void transition(View view) {
TransitionDrawable transitionDrawable = (TransitionDrawable) mImageView.getDrawable();
transitionDrawable.startTransition(500);
}
public void animation1(View view) {
Animation animation = AnimationUtils.loadAnimation(this, R.anim.translate);
mImageView.startAnimation(animation);
}
public void animation2(View view) {
Animation animation = AnimationUtils.loadAnimation(this, R.anim.scale);
mImageView.startAnimation(animation);
}
}
# activity_animation.xml
<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">
<Button
android:id="@+id/button1"
android:onClick="transition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Transition"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent" />
<ImageView
android:id="@+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:srcCompat="@drawable/transition" />
<Button
android:id="@+id/button12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:onClick="animation1"
android:text="Animation1"
app:layout_constraintLeft_toRightOf="@+id/button1"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button13"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:onClick="animation2"
android:text="Animation2"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button1" />
<EditText
android:id="@+id/edit_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:ems="10"
android:inputType="textPersonName"
android:hint="name"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button13" />
</androidx.constraintlayout.widget.ConstraintLayout>
# transition.xml
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/girlfriend" />
<item android:drawable="@drawable/blizzard" />
</transition>
# scale.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000">
<scale
android:fromXScale="5.0"
android:fromYScale="5.0"
android:pivotX="0"
android:pivotY="0"
android:toXScale="1.0"
android:toYScale="1.0" />
</set>
# shake.xml
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXDelta="0"
android:interpolator="@android:anim/cycle_interpolator"
android:toXDelta="10" />
# translate.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="700">
<alpha
android:fromAlpha="0"
android:toAlpha="1" />
<translate
android:fromXDelta="-100%"
android:toXDelta="0" />
</set>
49일차 조도 센서
# SensorActivity.java
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/java/com/example/myapplication/activities/SensorActivity.java
# activity_sensor.xml
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/res/layout/activity_sensor.xml
public class SensorActivity extends AppCompatActivity
implements SensorEventListener {
private static final String TAG = SensorActivity.class.getSimpleName();
private SensorManager mSensorManager;
private Sensor mLightSensor;
private View mRoot;
private float mCurrentLux = 0f;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sensor);
mRoot = findViewById(R.id.root);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
}
@Override
protected void onResume() {
super.onResume();
// 센서 이벤트 등록
mSensorManager.registerListener(this, mLightSensor, SensorManager.SENSOR_DELAY_UI);
}
@Override
protected void onPause() {
super.onPause();
// 센서 이벤트 해제
mSensorManager.unregisterListener(this);
}
private long mPrevTime = System.currentTimeMillis();
@Override
public void onSensorChanged(SensorEvent event) {
// 1초에 한번씩
long time = System.currentTimeMillis();
if (time - mPrevTime < 1000) {
return;
}
mPrevTime = time;
// 조도
float lux = event.values[0];
getSupportActionBar().setTitle("조도 : " + lux + " lx");
// 부드럽게 전환되는 Drawable
ColorDrawable[] drawables = new ColorDrawable[]{
new ColorDrawable(color(mCurrentLux)),
new ColorDrawable(color(lux))
};
TransitionDrawable drawable = new TransitionDrawable(drawables);
drawable.startTransition(1000);
mRoot.setBackground(drawable);
mCurrentLux = lux;
}
private int color(float lux) {
if (lux < 255f / 7f) {
return Color.MAGENTA;
} else if (lux < 255f / 7f * 2f) {
return Color.parseColor("#4b0082");
} else if (lux < 255f / 7f * 3f) {
return Color.BLUE;
} else if (lux < 255f / 7f * 4f) {
return Color.GREEN;
} else if (lux < 255f / 7f * 5f) {
return Color.YELLOW;
} else if (lux < 255f / 7f * 6f) {
return Color.parseColor("#ff8c00");
}
return Color.RED;
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
50일차 TedPermission 권한 체크
# TedPermission
https://github.com/ParkSangGwon/TedPermission
# PermissionActivity.java
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/java/com/example/myapplication/activities/PermissionActivity.java
# activity_permission.xml
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/res/layout/activity_permission.xml
public class PermissionActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_permission);
}
public void checkPermission(View view) {
TedPermission.with(this)
.setRationaleMessage("이 기능은 외부 저장소에 접근 권한이 필요합니다.")
.setDeniedMessage("설정 메뉴에서 언제든지 권한을 변경할 수 있습니다")
.setPermissions(Manifest.permission.READ_EXTERNAL_STORAGE)
.setPermissionListener(new PermissionListener() {
@Override
public void onPermissionGranted() {
// 기능 수행
method();
}
@Override
public void onPermissionDenied(List<String> deniedPermissions) {
}
}).check();
}
private void method() {
}
}
51일차 Firebase Functions
# Cloud Functions for Firebase
https://firebase.google.com/docs/functions
52일차 빌드 자동화 gradle
# app/build.gradle
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/build.gradle
# gradle.properties
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/gradle.properties
# Sign APK without putting keystore info in build.gradle
https://stackoverflow.com/questions/20562189/sign-apk-without-putting-keystore-info-in-build-gradle/38046932
# gradle.properties (Global Properties)
모든 프로젝트에 적용한다. 검색할 것
# 오른쪽 윈도우 (Gradle 창) 에서 빌드하기
프로젝트명 – app – build – assembleRelease // 빌드하기
프로젝트명 – app – install – installRelease // ADB 명령으로 하지 않아도 자동으로 릴리스용으로 기기로 인스톨 됨
# 안드로이드 스튜디오 실행 없이 핸드폰으로 앱 설치
gradlew InstallRelease
gradlew InstallDebug
// 리눅스는 ./gradlew InstallRelease
android {
signingConfigs {
release {
storeFile file('yourfile.keystore')
storePassword ANDROID_STORE_PASSWORD
keyAlias 'youralias'
keyPassword ANDROID_KEY_PASSWORD
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
# app/build.gradle
signingConfigs {
release {
storeFile file("../keystore/test_key.jks") // 키스토어 위치
storePassword _storePassword
keyAlias _keyAlias
keyPassword _keyPassword
}
}
buildTypes {
// APK 생성 시 버전명 자동 기입하기
applicationVariants.all { variant ->
variant.outputs.each { output ->
output.outputFile = new File(
output.outputFile.parent,
output.outputFile.name.replace(".apk",
"-${variant.versionName}.apk")
)
}
}
release {
signingConfig signingConfigs.release // 여기
minifyEnabled true // 난독화
zipAlignEnabled true // zipAlign
shrinkResources true // 미사용 리소스 제거
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
# minifyEnabled true // 난독화를 켰을 때 오류가 나는 것을 무시
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
}
# gradle.properties
org.gradle.parallel=true org.gradle.daemon=true org.gradle.jvmargs=-Xmx3072m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 _storePassword=1234 _keyAlias=key _keyPassword=1234 android.enableBuildCache=true