2026/02/15

오늘의 이야기




앱을 만들다 보니 로딩 화면에서 광고문구등을 넣어서 사용하고 싶은 요청이 있다. 이런건 어떻게 ? 그냥 쉬운 생각으로 빈 activity 을 만들어서 잠시 보여주고 그냥 닫아 주면 되지 않을까 ? 그래서 다소 빈약한 layout을 가지는 activity을 하나 만들었다.


먼저 layout 


<?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"
android:paddingLeft="18dp"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="18dp"
android:paddingBottom="@dimen/activity_vertical_margin"

tools:context=".LoadingPage">

<TextView
android:id="@+id/textView"
android:layout_width="180dp"
android:layout_height="65dp"
android:text="@string/loading"
android:textSize="34sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.593"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.421" />

<TextView
android:id="@+id/textView8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="36dp"
android:layout_marginTop="420dp"
android:gravity="center_horizontal|center_vertical"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="36dp"
android:layout_marginTop="460dp"
android:gravity="center_horizontal|center_vertical"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/textView10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="36dp"
android:layout_marginTop="500dp"
android:gravity="center_horizontal|center_vertical"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>



 결과물은 이런 정도 메시지가 들어가는 layout


예시물에는 사용자의 요청에 따라 일부 문구가 들어가기는 하지만, 


이건 단순 예제 이니 참고만...


 


 


 


 


 


 


 


 


 


다음은 activity을 만들면...


public class LoadingPage extends Activity implements View.OnClickListener {

private static final String TAG = "LoadingPage";
ActivityLoadingPageBinding binding ;
SharedPreferences sp ;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sp = getSharedPreferences(getPackageName(), MODE_PRIVATE);
binding = ActivityLoadingPageBinding.inflate(getLayoutInflater());
View view = binding.getRoot() ;

setContentView(view);

}

@Override
protected void onStart() {
super.onStart();
startLoading();
}

private void startLoading() {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
finish();
}
}, 3000);
}

}

이런 정도의 코드... acitivity 의 lifecycle 을 보고 어디쯤에서 종료를 하게 만들껀가 그것을 고민했고, 다음은 열리자 마자 닫아 버리면 안되니까... handler 을 이용해서 약간의 지연 (3000ms = 3sec) 을 두고 앱을 종료해 주는 것으로 끝.


그럼 이걸 어떻게 호출하지 하는 건 다음과 같이 MainActvity 에서 호출하는 방식으로 처리를 했다.


    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

...

Intent intent = new Intent(this, LoadingPage.class);
startActivity(intent);

...

}

이렇게 MainActivity 을 시작하자 마자 LoadingPage 을 호출하는 것으로 그렇게 하면 사용자는 메인화면위에 올라온 loading 페이지를 잠시 보고 사라지는 것을 보게될 것이다. 


 


이것으로 끝,,,


 


 


 





반응형





























오늘의 이야기










앱을 만들어보다보니... 색상을 입히는 것도 테크니컬 해야 할 것 같은 생각이 들었다. 


쭈욱 작업을 하고 마무리를 하는 시점이 되었을 때 배경색을 변경해 달라고 하거나, 글짜색이 마음에 들지 않는 다고 한다. 흑~... theme 을 모르던 시절에는 하나하나 선택해서 font 도 지정하고 나름 color 도 지정해서 만들어 더니만.


이런이런... 


그럼 준비를 해 볼까 ?


먼저 theme.xml 을 이용해서 사용하고 싶은 style 을 만들어 본다.


<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.Weroom714" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/default_color</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/white</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
<item name="android:fontFamily">@font/poorstory_regular</item>
<item name="android:background">@color/default_color</item>
<item name="android:windowBackground">@color/default_color</item>
<item name="android:colorBackground">@color/default_color</item>
<item name="android:textColor">@color/white</item>
<item name="colorAccent">@color/softBlue</item> <!-- -->
<item name="android:textColorPrimaryDisableOnly">@color/white</item> <!-- checkbox text color -->
<item name="android:textColorSecondary">@color/softBlue</item>
<item name="android:buttonTint">@color/white</item><!-- checkbox 의 체크상자 색상 -->
</style>
<style name="Theme.Weroom714.purple" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/white</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
<item name="android:fontFamily">@font/poorstory_regular</item>
<item name="android:background">@color/purple_200</item>
<item name="android:windowBackground">@color/purple_200</item>
<item name="android:colorBackground">@color/purple_200</item>
<item name="android:textColor">@color/white</item>
<item name="colorAccent">@color/white</item> <!-- -->
<item name="android:textColorPrimaryDisableOnly">@color/white</item> <!-- checkbox text color -->
<item name="android:textColorSecondary">@color/softBlue</item>
<item name="android:buttonTint">@color/white</item><!-- checkbox 의 체크상자 색상 -->
</style>
<style name="Theme.Weroom714.softBlack" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/softBlack</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/white</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
<item name="android:fontFamily">@font/poorstory_regular</item>
<item name="android:background">@color/softBlack</item>
<item name="android:windowBackground">@color/softBlack</item>
<item name="android:colorBackground">@color/softBlack</item>
<item name="android:textColor">@color/white</item>
<item name="colorAccent">@color/white</item> <!-- -->
<item name="android:textColorPrimaryDisableOnly">@color/white</item> <!-- checkbox text color -->
<item name="android:textColorSecondary">@color/softBlue</item>
<item name="android:buttonTint">@color/white</item><!-- checkbox 의 체크상자 색상 -->
</style>
<style name="Theme.Weroom714.darkBlue" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/darkBlue</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/darkBlue</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/white</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
<item name="android:fontFamily">@font/poorstory_regular</item>
<item name="android:background">@color/darkBlue</item>
<item name="android:windowBackground">@color/darkBlue</item>
<item name="android:colorBackground">@color/darkBlue</item>
<item name="android:textColor">@color/white</item>
<item name="colorAccent">@color/white</item> <!-- -->
<item name="android:textColorPrimaryDisableOnly">@color/white</item> <!-- checkbox text color -->
<item name="android:textColorSecondary">@color/softBlue</item>
<item name="android:buttonTint">@color/white</item><!-- checkbox 의 체크상자 색상 -->
</style>
<style name="Theme.Weroom714.darkGreen" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/darkGreen</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/darkGreen</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/white</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
<item name="android:fontFamily">@font/poorstory_regular</item>
<item name="android:background">@color/darkGreen</item>
<item name="android:windowBackground">@color/darkGreen</item>
<item name="android:colorBackground">@color/darkGreen</item>
<item name="android:textColor">@color/white</item>
<item name="colorAccent">@color/white</item> <!-- -->
<item name="android:textColorPrimaryDisableOnly">@color/white</item> <!-- checkbox text color -->
<item name="android:textColorSecondary">@color/softBlue</item>
<item name="android:buttonTint">@color/white</item><!-- checkbox 의 체크상자 색상 -->
</style>
<style name="Theme.Weroom714.Loading" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:background">@color/default_color</item>
<item name="android:windowBackground">@color/default_color</item>
<item name="android:colorBackground">@color/default_color</item>
<item name="colorPrimary">@color/default_color</item>
<item name="android:textColor">@color/softBlue</item>
<item name="android:fontFamily">@font/poorstory_regular</item>
</style>
<style name="ButtonText" parent="@android:style/Widget.Button">
<item name="android:textColor">@color/white</item>
<item name="android:background">@drawable/background_btn</item>
</style>
<style name="CustomRadioButton" parent="Theme.AppCompat.Light">
<item name="colorAccent">@color/white</item>
<item name="colorControlNormal">@color/default_color</item>
</style>
<style name="Theme.Weroom714.Transparent" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:textColor">@color/softBlue</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:fontFamily">@font/poorstory_regular</item>
</style>
<style name="AppCompatAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="colorAccent">@color/softBlue</item>
<item name="android:textColor">@color/softBlue</item>
<item name="android:textColorPrimary">@color/white</item>
<item name="android:background">@color/default_color</item>
</style>
</resources>

이렇게 길어야 하는 생각이 들기는 하지만, 아직 조금의 미숙(?)함이 있는 관계로 다가 비슷한 sylte 을 복사해서 여러개 만들었다.


기본테마 Theme.Weroom714 부터 Theme.Weroom714_darkGreen, Theme.Weroom714_darkBlue, Theme.Weroom714_softBlack 까지 사용자는 4개의 색상 조합을 요청했다.  자세히 살펴 보면 다른 건 없고, 배경색만 다르게 지정하는 구성이다. 


이걸 어떻게 지정하는 건가 ?


    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sp = getSharedPreferences(getPackageName(), MODE_PRIVATE);
if (sp.getString("THEME", "default_color").equals("darkBlue")) {
setTheme(R.style.Theme_Weroom714_darkBlue);
} else if (sp.getString("THEME", "default_color").equals("darkGreen")) {
setTheme(R.style.Theme_Weroom714_darkGreen);
} else if (sp.getString("THEME", "default_color").equals("softBlack")) {
setTheme(R.style.Theme_Weroom714_softBlack);
} else {
setTheme(R.style.Theme_Weroom714);
}
binding = ActivityMainBinding.inflate(getLayoutInflater());
View view = binding.getRoot() ;
setContentView(view);

}

적용하려는 activity 의 onCreate 에 위와 같이 적용하면 된다. 여기서 주의할 것은 viewBinding 을 하고 있기 때문에 그 위치를 잡는데, 테스트를 몇차례해서 정한건데, layout 을 binding 하기전에 setTheme()을 이용해서 먼저 설정하고 binding을 해야 실행 했을때 적용이 되는 것을 확인할 수 있었다.




이렇게 선택적으로 배경색을 변경하는 앱을 만들어 볼 수 있었다.


 


오늘도 즐~ 코딩...


 


p.s 이 미숙함을 지적해 주실꺼면 아래 댓글로 부탁 드립니다. 꾸벅^^;;





반응형























오늘의 이야기


#스하리1000명프로젝트,
迷失在韓國?即使您不會說韓語,這個應用程式也可以幫助您輕鬆出行。
只需說出您的語言即可 - 它會翻譯、搜尋並以您的語言顯示結果。
非常適合旅行者!支援英語、日語、中文、越南語等10多種語言。
現在就試試吧!
https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127




2026/02/14

오늘의 이야기



아무래도 휴대폰에 앱을 개발하는 일이다 보니, 문자 전송 같은 기본 기능을 이용하는 앱을 개발하는 일이 많기는 하다.  요새는 카카오톡등을 이용하거나, FCM 등을 이용해서 알림을 보내는 기능등을 구현해 보지만, 예전 방식 처럼 SMS 을 전송해 보는 것도 오랜만 이기는 하다.


구글이 계정 정책등으로 인해 앱을 등록할 때 기본앱으로 사용할 수 없는 SMS 앱은 등록에 무리가 있다. 그래서 그냥 개인적으로 사용하기 위해서 기본앱 기능은 아니지만, 필요에 의한 문자 전송을 구현하는 앱을 만들어 볼 까 싶다.


먼저 앱을 구동하기 위해서는 Manifest 파일에 권한 설정을 등록해 준다. 


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

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

<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/Theme.SMSSend0823">

......

</application>

</manifest>

SMS 발송 권한을 설정했으니, 앱을 실행하면서 실제 동작이 발생 하기 전에 사용자에게 권한을 획득 하는 코드를 activity 에 넣어 보자.


import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.navigation.fragment.NavHostFragment;

import com.example.smssend0823.Database.DBHandler;
import com.example.smssend0823.Database.MsgTargetBean;
import com.example.smssend0823.Database.SendMessageAdapter;
import com.example.smssend0823.Database.SendMessageBean;
import com.example.smssend0823.Utils.OnBackPressedListener;
import com.example.smssend0823.databinding.FragmentMsgselectBinding;

import java.util.ArrayList;

public class MsgSelectFragment extends Fragment implements OnBackPressedListener {

private static final String TAG = "MsgSelectFragment";
private FragmentMsgselectBinding binding;
ArrayList<SendMessageBean> beans;
SendMessageAdapter adapter;
DBHandler dbHandler ;
String phoneNo ;
String message ;
private static final int PERMISSION_RQST_SEND = 101;

public static Fragment newInstance() {
MsgSelectFragment fragment = new MsgSelectFragment();
return fragment;
}

@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState
) {

binding = FragmentMsgselectBinding.inflate(inflater, container, false);
return binding.getRoot();

}

public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);

beans = new ArrayList<>();
doDisplaySendMessage();
binding.listSendMessage.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
int iCnt = 0 ;
for(int i=0; i < beans.size() ; i++) {
if (beans.get(i).isSelectedMsg()) iCnt++;
}
if (iCnt > 0) {
beans.get(position).setSelectedMsg(false);
adapter.notifyDataSetChanged(beans);
Toast.makeText(getContext(), iCnt +"개 이상 선택할 수 없습니다.", Toast.LENGTH_SHORT).show();
return;
}
beans.get(position).setSelectedMsg(!beans.get(position).isSelectedMsg());
adapter.notifyDataSetChanged(beans);
}
});

binding.btnSendMessage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = 0 ; int iCnt = 0 ;
for(int i=0; i < beans.size() ; i++) {
if (beans.get(i).isSelectedMsg()) {
position = i;
iCnt++;
break;
};
}
if (iCnt == 0) {
Toast.makeText(getContext(), "전송할 메시지를 선택하세요.", Toast.LENGTH_SHORT).show();
return;
}
message = beans.get(position).getSendMessage() ;
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("메시지 전송")
.setMessage("[" + beans.get(position).getSendMessage() + "] 로 전송할까요?")
.setPositiveButton("확인", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dbHandler = DBHandler.open(getContext());
Cursor rs = dbHandler.selectAll();
while(rs.moveToNext()) {
MsgTargetBean msgTargetBean = new MsgTargetBean();
msgTargetBean.setId(rs.getInt(rs.getColumnIndex("_id")));
msgTargetBean.setSendSeqNo(rs.getString(rs.getColumnIndex("sendSeqNo")));
msgTargetBean.setReceivePhoneNum(rs.getString(rs.getColumnIndex("receivePhoneNum")));
msgTargetBean.setSendMessage(rs.getString(rs.getColumnIndex("sendMessage")));
msgTargetBean.setSendTy(rs.getString(rs.getColumnIndex("sendTy")));
if("S".equals(msgTargetBean.getSendTy())) {
phoneNo = msgTargetBean.getReceivePhoneNum();
if (sendSMSMessage()) {
msgTargetBean.setSendTy("Y");
msgTargetBean.setSendMessage(message);
dbHandler.update(msgTargetBean);
}
}
}
dbHandler.close();
}
})
.setNegativeButton("취소", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {

}
});
AlertDialog dialog = builder.create();
dialog.show();
}
});
}

private void doDisplaySendMessage() {
beans.clear();
dbHandler = DBHandler.open(getContext());
Cursor rs = dbHandler.selectMsgAll();
while (rs.moveToNext()) {
SendMessageBean bean = new SendMessageBean();
bean.setSendMessage(rs.getString(rs.getColumnIndex("messageString")));
beans.add(bean);
}
adapter = new SendMessageAdapter(beans, getContext());
adapter.notifyDataSetChanged(beans);
binding.listSendMessage.setAdapter(adapter);
}

protected boolean sendSMSMessage() {
if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(getContext(), "SMS 전송 권한에 대한 허가가 필요합니다.", Toast.LENGTH_SHORT).show();
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),Manifest.permission.SEND_SMS)) {
Log.e(TAG, "shouldShowRequestPermissionRationale");
}
else { ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.SEND_SMS}, PERMISSION_RQST_SEND);
Log.e(TAG, "requestPermissions");
}
} else {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNo, null, message, null, null);
Toast.makeText(getContext(), "메시지가 전송 되었습니다.",Toast.LENGTH_LONG).show();
return true ;
}
return false;
}
//Now once the permission is there or not would be checked
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_RQST_SEND: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

} else {Toast.makeText(getContext(), "SMS 발송이 되지 않았습니다. 잠시 뒤에 다시 시도 하세요.", Toast.LENGTH_LONG).show();
return;
}
}
}
}

@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}

@Override
public void onBackPressed() {
getParentFragmentManager().beginTransaction()
.replace(R.id.container, MsgSendListFragment.newInstance())
.commitNow();
}
}

 


source code 의 예시와 같이 sendSMSMessage() 함수에는 메시지 발송전에 권한 획득 여부를 확인하고, 권한이 없다면 사용자에게 권한 허가를 요청하는 알림을 띄워 권한을 획득 하게 된다.










다만, 이런 기능을 앱으로 이용하려면 playstore 에는 등록에 제한이 있으므로 다른 스토어를 이용하여야 한다.


SMS을 보내는 다른 방법은 권한 없이 사용할 수 있는 SMS Retriever API 을 활용하는 방법이 있다고 할 수 있는데, 실제 앱을 개발하다 보면 불편한 사항이 생기게 되어 있다.


https://developers.google.com/identity/sms-retriever/overview



 


SMS Retriever API를 사용한 자동 SMS 확인  |  SMS Verification APIs


이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English SMS Retriever API를 사용한 자동 SMS 확인 SMS Retriever API를 사용하면 사용자가 인증 코드를 수동으로 입력 할 필요없이 추가 앱 권


developers.google.com




참고해서 보면 좋을 것 깥다.


 















오늘의 이야기



Android 스튜디오: 절전 모드


Android Studio: 절전 모드는 Android 스튜디오 절전 모드입니다. 코드를 연 후 자동으로 프롬프트가 표시되지 않고 어떤 클래스와 메서드가 참조되는지 직관적이지 않으며 코드 자체 검사가 적용되지 않습니다.


인터넷을 찾다가 발견한 설명이다. 




 


이걸 체크 하면 불편한 점...  코드 입력시 auto coding 을 지원 하지 않는다.  이것이 제일 불편하다고 할 수 있을 것 같다.










 























오늘의 이야기



Android 스튜디오: 절전 모드


Android Studio: 절전 모드는 Android 스튜디오 절전 모드입니다. 코드를 연 후 자동으로 프롬프트가 표시되지 않고 어떤 클래스와 메서드가 참조되는지 직관적이지 않으며 코드 자체 검사가 적용되지 않습니다.


인터넷을 찾다가 발견한 설명이다. 




 


이걸 체크 하면 불편한 점...  코드 입력시 auto coding 을 지원 하지 않는다.  이것이 제일 불편하다고 할 수 있을 것 같다.




반응형






 























오늘의 이야기











반응형







앱을 하나 만들다 보니.... 전체 화면에 보일 수 있도록 만들어 달라는 요청이 있네... 어떻게 하나 ?


구글님에게 질문을 해 본다... 그러고선 이렇게 구현을 해 보았다.


    @Override
protected void onPostResume() {
super.onPostResume();

Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
hideSystemUI();
}
}, 500);
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
hideSystemUI();
}
}

private void hideSystemUI() {

View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);

decorView.requestLayout();
}

 


먼저 activity 의 lifecycle 을 대해서 찾아볼 필요가 있다. 왜 그런가 하면 액티비티가 생성되면서 어느 시점에 화면을 다시 그리게 할 것인가를 정해야 할 것 같아서 이다.  개발자 페이지에서 받아온 이미지 하나를 링크을 달았다.


저 그림의 순서을 보면 onCreate 나 onStart 에 화면 다시 그리기를 넣어 놓으면 처음 실행할 때는 적용이 되겠지만, 다른 액티비티로 갔다가 돌아올때는 실행이 되지 않는 다, 그래서 혹시나, 이 액티비티에서 다른 액티비티를 호출했다가 돌아와야 하는 경우가 있다면 필수적으로 onResume에 넣어 주는 것이 좋을 것 같다.


이미지 출처 : https://developer.android.com/guide/components/activities/activity-lifecycle



 


그리고 위의 소스에서 onWindowFocusChanged 는 구글링에서 나온 부분일껀데, 아마도 윈도우가 다시 활성화 되는 시점에 처리를 하고 넣은 게 아닐까 ? 


뭐 아무튼 이렇게 처리를 하게 되면 앱이 실행되고 나서 화면 전체를 차지하게 된다.


내가 만든 앱 실행화면



이런식으로 위/아래 있는 상태바, 네비게이션바등이 다 숨어지게 된다.


 


끝.





반응형























오늘의 이야기



#스치니1000프로젝트 #재미 #행운기원 #Compose #Firebase

🎯 야 너 토요일마다 로또 확인하냐?
나도 맨날 "혹시나~" 하면서 봤거든 ㅋㅋ

근데 이제는 그냥 안 해
AI한테 맡겼어 🤖✨

그것도 구글 Gemini로다가!

그래서 앱 하나 만들었지
👉 "로또 예상번호 by Gemini" 🎱

AI가 분석해서 번호 딱! 뽑아줌
그냥 보고 참고만 하면 됨

재미로 해도 좋고…
혹시 모르는 거잖아? 😏


https://play.google.com/store/apps/details?id=com.billcorea.gptlotto1127




오늘의 이야기





반응형







 




집에 오는 길에... 길가에 있던 들꽃 


이름은 무엇인가 ?


날이 더운데 잘 지내는 지 ?


오늘 하루도 좋은 하루가 되었길 바라며...


다음에 다시 만나게 되면 더 좋은 이야길 할 수 있게 되길 바라며...





반응형





























오늘의 이야기





반응형







앱을 만들다가... 갤러리에서 이미지 받아오는 와서 사용하는 것을 구현하고 있는 중인데...


사진이 돌아간다. 흑~ 그래서 구글링 신에서 질문을 했다...


답... Exif 을 구현해서 사진을 돌리는 코드가 나오고... 


그것 보다는 조금 간결한 코드 구현 방법이 보인다.


http://bumptech.github.io/glide/doc/getting-started.html



 


Glide v4 : Getting Started


Basic Usage Loading images with Glide is easy and in many cases requires only a single line: Glide.with(fragment) .load(myUrl) .into(imageView); Cancelling loads you no longer need is simple too: Glide.with(fragment).clear(imageView); Although it's good


bumptech.github.io




glide 이건 뭔가 ???  쉬운 이미지 처리 방법 


 


백그라운드 스레드
배경 스레드에 이미지를 로드하는 것도 submit(int, int)다음을 사용하여 간단합니다 .

FutureTarget<Bitmap> futureTarget =
Glide.with(context)
.asBitmap()
.load(url)
.submit(width, height);

Bitmap bitmap = futureTarget.get();

이렇게 설명된 부분을 참조 하면 될 것 같다. 또 하나 방법은 바로 imageView 에 넣는 방법인데...  


알고 싶은 건 Bitmap 에 값을 넣는 것이니... 그런데... 백그라운드 스레드로 해야 한다는 부분을 주의 해야 했다.


그래서 아래 처럼 코딩해서 해소 끝...


            FutureTarget<Bitmap> futureTarget =
Glide.with(getApplicationContext())
.asBitmap()
.load(photoUri)
.submit(width, height);

Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {

try {
Bitmap bitmap = futureTarget.get();

GroupImageBean bean1 = new GroupImageBean();
bean1.setImageGrpId(groupName);
bean1.setImageData(bitmap);
bean1.setOriginalUri(photoUri);
bean1.setImageUri(photoUri);
bitmapArrayList.add(bean1);

dbHandler = DBHandler.open(SetGroupActivity.this);
dbHandler.insertImage(bean1);
dbHandler.close();

getAdapterUpdateReceiptsList();
} catch (Exception e) {
e.printStackTrace();
}

}
}, 660);

 


다음에 또 이런걸 하게 될 지 모르지만... 


 


아래 코드는 갤러리에서 사진 선택할 수 있도록 호출 하기 위한 코드 ...


 


                Intent intent = new Intent();
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) // 이건 동시에 여러장 선택
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(intent, REQUEST_CODE);

저렇게 호출하면 그걸 받아서 처리하는 부분은 


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if(requestCode == REQUEST_CODE)
{
if(resultCode == RESULT_OK)
{
try{
InputStream in = getContentResolver().openInputStream(data.getData());

Bitmap img = BitmapFactory.decodeStream(in);
in.close();

imageView.setImageBitmap(img);
}catch(Exception e)
{

}
}
else if(resultCode == RESULT_CANCELED)
{
Toast.makeText(this, "사진 선택 취소", Toast.LENGTH_LONG).show();
}
}
}

이렇게 activity 호출 결과를 받아서 처리하면 됨...


오늘 도 즐~~~ 코딩...





반응형





























오늘의 이야기


#스하리1000명프로젝트

스치니들!
내가 만든 이 앱은, 내 폰에 오는 알림 중에서 중요한 키워드가 있는 경우
등록해둔 친구에게 자동으로 전달해주는 앱이야 📲

예를 들어, 카드 결제 알림을 와이프나 자녀에게 보내주거나
이번 달 지출을 달력처럼 확인할 수도 있어!

앱을 함께 쓰려면 친구도 설치 & 로그인해줘야 해.
그래야 친구 목록에서 서로 선택할 수 있으니까~
서로 써보고 불편한 점 있으면 알려줘 🙏

👉 https://play.google.com/store/apps/details?id=com.nari.notify2kakao





오늘의 이야기

https://medium.com/nybles/sending-push-notifications-by-using-firebase-cloud-messaging-249aa34f4f4c   Sending Push Notifications by Using Fi...