반응형

GetAppInfo 클래스에서는 넘겨받은 패키지명을 가지고 Play 스토어의 해당 페이지로 가서 앱 이름과 버전에 해당하는 element의 값을 Jsoup 라이브러리를 이용해서 구하게 됩니다.

public class GetAppInfo extends AsyncTask<Void, String, Boolean> {
    private String appPackageName;
    private String appName;
    private String appVersion;

    public GetAppInfo(String packageName) {
        appPackageName = packageName;
    }

    @Override
    protected Boolean doInBackground(Void... strings) {
        Elements elementsAppName;
        Elements elementsAppVersion;
        boolean result = false;
        try {
            String AppFromPlayStore = "https://play.google.com/store/apps/details?id=" + appPackageName;
            Document doc = Jsoup.connect(AppFromPlayStore).get();
            elementsAppName = doc.select("div > div.sIskre > c-wiz:nth-child(1) > h1 > span");
            if (elementsAppName.size() != 0) {
                appName = elementsAppName.text();
            } else {
                elementsAppName = doc.select("div > div.sIskre > c-wiz:nth-child(2) > h1 > span");
                if (elementsAppName.size() != 0) {
                    appName = elementsAppName.text();
                } else {
                    appName = "Crawling code(App Name) needs to be changed.";
                }
            }
            elementsAppVersion = doc.select("div > div:nth-child(4) > span > div > span");
            if (elementsAppVersion.size() != 0) {
                appVersion = elementsAppVersion.text();
            } else {
                appName = "Crawling code(App Version) needs to be changed.";
            }
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

실제 테스트한 앱은 아래와 같은 화면을 구성하고 있습니다.

패키지명을 입력하고 버튼을 누르면 Play 스토어에서 크롤링해 온 앱 이름과 버전을 표시해줍니다.

왼쪽: 버튼 누르기 전, 오른쪽: 버튼 누른 후 

아래처럼 여러 경우에 따라서 App name과 App version에 표시해주는 정보가 달라집니다.

        btnGetAppInfo = findViewById(R.id.btnGetAppInfo);
        btnGetAppInfo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                hideKeyboard();

                if (isNetwork()) {
                    try {
                        getAppInfo = new GetAppInfo(etPackageName.getText().toString());
                        if (getAppInfo.execute().get()) {
                            tvAppName.setText(getAppInfo.getAppName());
                            tvAppVersion.setText(getAppInfo.getAppVersion());
                        } else {
                            tvAppName.setText("N/A (The package name app does not exist on the Play Store.)");
                            tvAppVersion.setText("N/A (The package name app does not exist on the Play Store.)");
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        tvAppName.setText("N/A (Error)");
                        tvAppVersion.setText("N/A (Error)");
                    }
                } else {
                    tvAppName.setText("N/A (No internet connection)");
                    tvAppVersion.setText("N/A (No internet connection)");
                }
            }
        });

 

위의 예제 소스를 첨부하였으니 참고하세요.

CrawlingAppInfo.zip
0.14MB

 

 

반응형

[Snippet]

 

화면 밝기 변경

밝기 값

0~255(최소~최대), -1(시스템 설정값)

private void changeScreenBrightness(int value) {
Window window = getWindow();
WindowManager.LayoutParams layoutParams = window.getAttributes();
layoutParams.screenBrightness = value * 1.0f / 255;
window.setAttributes(layoutParams);
}

 

[Sample]

버튼 3개(최소, 최대, 시스템 설정)로 해당 밝기 선택, 종료시는 시스템 설정 밝기로 복귀

public class MainActivity extends AppCompatActivity {
private static final int BRIGHTNESS_MIN = 0;
private static final int BRIGHTNESS_MAX = 255;
private static final int BRIGHTNESS_SYSTEM = -1;

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

findViewById(R.id.btnMinBrightness).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
changeScreenBrightness(BRIGHTNESS_MIN);
}
});

findViewById(R.id.btnMaxBrightness).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
changeScreenBrightness(BRIGHTNESS_MAX);
}
});

findViewById(R.id.btnSystemBrightness).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
changeScreenBrightness(BRIGHTNESS_SYSTEM);
}
});
}

@Override
protected void onStop() {
super.onStop();
changeScreenBrightness(BRIGHTNESS_SYSTEM);
}

private void changeScreenBrightness(int value) {
Window window = getWindow();
WindowManager.LayoutParams layoutParams = window.getAttributes();
layoutParams.screenBrightness = value * 1.0f / 255;
window.setAttributes(layoutParams);
}

ScreenBrightness.zip 

 

반응형

[Snippet]

일정 시간 후에 실행하기

#1 메인 스레드(UI 스레드)인 경우
Handler().postDelayed(new Runnable() {
	@Override
	public void run() {
		//실행할 코드
	}
}, 1000);

 

#2 메인 스레드가 아닌 경우

 

메인 스레드가 아닌 곳에서 사용시 해당 스레드에서 looper가 구현되어 있지 않으면 위의 코드 실행시 아래와 같은 에러를 만나게 됩니다.

01-24 13:06:48.589 E 32489    32729    AndroidRuntime: FATAL EXCEPTION: Thread-4274

01-24 13:06:48.589 E 32489    32729    AndroidRuntime:  Process: kr.happydev.snippettest, PID: 32489
01-24 13:06:48.589 E 32489    32729    AndroidRuntime: java.lang.RuntimeException: Cant create handler inside thread that has not called Looper.prepare()

 

이때는 Handler 객체 생성자의 인수로 looper를 구현해서 넣어주어야 합니다.

또는 메인 스레드의 루퍼를 사용할 경우는 아래와 같이 해주면 됩니다.

new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
    @Override
    public void run() {
        //실행할 코드
    }
}, 1000);

참고로 메인 스레드의 경우 #1 코드의 생성자 인수로 looper를 전달하지 않아도 RuntimeException이 발생하지 않는 것은 메인 스레드의 looper를 따로 생성하지 않아도 이미 존재하기 때문에 문제가 되지 않습니다.

 

 

[Sample]

#1 Back 키를 연속해서 두 번 누를 경우 종료하도록 처리하는 예

private boolean backKeyPressedTwice = false;

@Override
public void onBackPressed() {
    if (backKeyPressedTwice) {
        super.onBackPressed();
        return;
    }

    backKeyPressedTwice = true;
    Toast.makeText(this, "Back 키을 한번 더 누르시면 종료됩니다", Toast.LENGTH_SHORT).show();

    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            backKeyPressedTwice = false;
        }
    }, 2000);
}

 (1) 처음에 Back 키를 누르면  backKeyPressedTwice 변수는 true, "Back 키를 한번 더 누르시면 종료됩니다" 토스트 발생

 (2) 2초 후에 backKeyPressedTwice 변수는 false로 초기화하도록 생성한 Runnable 객체를 핸들러에 전달 

 (3-1) 2초 전에 Back 키를 한번 더 누르면 backKeyPressedTwice 변수는 아직 true이므로 super.onBackPressed()가 호출되면서 종료처리 됨

 (3-2) 2초 후에 Back 키를 한번 더 누르면 (1)과 같이 실행됨

 

 

#2 일정 시간 후에 종료하도록 처리하는 예

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

    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {
        Toast.makeText(getApplicationContext(), "There is no camera flash.\n
                        The app will finish!", Toast.LENGTH_LONG).show();
        delayedFinish();
        return;
    }
    ...
}

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

앱 실행후 카메라 플래시 Feature가 존재하지 않는 경우 "There is no camera flash. The app will finish!" 문구의 토스트를 띄우고 토스트가 사라질 때쯤에 종료(finish)하도록 만든 것입니다.

 

위의 예는 아랫글에서 실제 사용하고 있는 코드입니다. 

 

2018/10/15 - 손전등 앱 만들기 #3 - Flashlight API 이용방법 (Flashlight app using Flashlight API)

 

[실행결과]

 

 

 

 

반응형

[Snippet]

앱 진입시 "스크린 리더" 감지하기

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

AccessibilityManager accessibilityManager = (AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE);
boolean isScreenReaderEnabled = accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled();

if (isScreenReaderEnabled) {
// 스크린 리더 활성화시
} else {
// 스크린 리더 비활성화시
}
}

 

 

[Sample]

"스크린 리더" 활성화에 따른 UI 변경

public class MainActivity extends AppCompatActivity {
private TextView mTextView;
private ImageButton mImageButton;
private Button mButton;

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

mTextView = findViewById(R.id.textView);
mImageButton = findViewById(R.id.imageButton);
mButton = findViewById(R.id.button);
}

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

AccessibilityManager accessibilityManager = (AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE);
boolean isScreenReaderEnabled = accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled();

if (isScreenReaderEnabled) {
mTextView.setText(getResources().getString(R.string.screen_reader_enable));
mButton.setVisibility(View.VISIBLE);
mImageButton.setVisibility(View.INVISIBLE);
} else {
mTextView.setText(getResources().getString(R.string.screen_reader_disable));
mButton.setVisibility(View.INVISIBLE);
mImageButton.setVisibility(View.VISIBLE);
}
}
}

프로젝트 화일
ScreenReaderDetectTest.zip

 

 

[실행결과]

 

 

 

반응형

[Snippet]
 "외부 전원 연결(끊김)" 감지하기

private final BroadcastReceiver mPowerConnectionReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())) {
//전원 연결시 처리
} else if (Intent.ACTION_POWER_DISCONNECTED.equals(intent.getAction())) {
//전원 끊김시 처리

}
};
protected void onResume() {

... 

 

IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);

registerReceiver(mPowerConnectionReceiver, filter);
}

protected void onPause() {

...

 

unregisterReceiver(mPowerConnectionReceiver);

}

 

 

 "헤드셋 연결(끊김)" 감지하기

private final BroadcastReceiver mHeadsetConnectionReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_HEADSET_PLUG.equals(intent.getAction())) {
switch(intent.getIntExtra("state", 0)) {
case 0:
//헤드셋 연결시 처리
break;
case 1:
//헤드셋 끊김시 처리
break;
default:
break;
}
}
}
};

protected void onResume() {
...

 

IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
registerReceiver(mHeadsetConnectionReceiver, filter);
}

protected void onPause() {

...

 

unregisterReceiver(mHeadsetConnectionReceiver);

}

 

 

 

[Sample]

 외부 전원 / 헤드셋 연결(끊김) 감지시 토스트 메시지 표시하기

public class MainActivity extends AppCompatActivity {
private final BroadcastReceiver mConnectionReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())) {
Toast.makeText(context, "AC Power connected.", Toast.LENGTH_LONG).show();
} else if (Intent.ACTION_POWER_DISCONNECTED.equals(intent.getAction())) {
Toast.makeText(context, "AC Power disconnected.", Toast.LENGTH_LONG).show();
} else if (Intent.ACTION_HEADSET_PLUG.equals(intent.getAction())) {
Toast.makeText(context, "Headset " + ((intent.getIntExtra("state", 0) == 1) ? "connect" : "disconnect") + " detected.",
Toast.LENGTH_LONG).show();
}
}
};

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

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

IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
filter.addAction(Intent.ACTION_HEADSET_PLUG);
registerReceiver(mConnectionReceiver, filter);
}

@Override
protected void onPause() {
unregisterReceiver(mConnectionReceiver);
super.onPause();
}
}

프로젝트 화일

ConnectionDetectTest.zip

 

[실행결과]

 

 

 

반응형

[Snippet]

"애플리케이션 정보" 보기로 이동

                Intent intent = new Intent();
                intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                intent.setData(Uri.fromParts("package""package name", null));
                startActivity(intent);

 

[Sample]

 - 앱 자신의 "애플리케이션 정보" 보기로 이동

public class MainActivity extends AppCompatActivity {

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

        setContentView(R.layout.activity_main);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                intent.setData(Uri.fromParts("package", getPackageName(), null));
                startActivity(intent);
            }
        });
    }
}

 

 - 현재 앱에서 TISTORY앱의 "애플리케이션 정보" 보기로 이동

public class MainActivity extends AppCompatActivity {
    private static final String TISTORY_PACKAGE_NAME = "net.daum.android.tistoryapp";

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

        setContentView(R.layout.activity_main);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                intent.setData(Uri.fromParts("package", TISTORY_PACKAGE_NAME, null));
                startActivity(intent);
            }
        });
    }
}

 

프로젝트 화일

AppInfoTest.zip

 

[실행결과]

 

      

+ Recent posts