티스토리 뷰

 Android에서는 Version 별로 제공하는 API 가 상이합니다. ICS인 4.0에서는 Notification에 큰 사진을 올리거나, 두손가락으로 내려서 창을 확대하여 추가적인 내용을 볼 수 없지만 JellyBean에서는 큰 사진, 추가 내용을 모두 확인할 수 있습니다. 2.3.x 대 의 점유율이 낮아지고, 4.4.x 대의 점유율이 높아지고는 있지만 그 중간의 4.0.x, JellyBean의 4.1~4.3 버전의 점유율은 높습니다. 4.0.x대가 아닌 4.1이상으로 코드를 작성한다고 해도 문제는 없지만 4.0.x 사용자를 버릴 수는 없죠. 


 그래서 Android에서는 그런것들을 해결할 수 있도록 추가 라이브러리를 제공해주고 있습니다. 초기에는 공식 API 문서가 없었던것으로 기억하나 지금은 d.android.com에 그 라이브러리의 내용까지 모두 포함되어 설명해주고 있습니다.


 v4 라이브러리를 사용하여 Notification을 분기 없이 4.0.x 버전과 최신버전까지 모두 사용하는 방법을 살펴보겠습니다.


ICS와 Jelly Bean에서 큰 사진 Notification 처리

 ICS는 위에서 설명하였 듯, Notification에서 상세 보기가 불가능합니다. 대신 v4 라이브러리를 사용하면 아래와 같은 결과를 얻을 수 있습니다. 큰 사진은 볼 수 없지만 같은 코드로 분기없이 아래와 같은 결과를 얻을 수 있습니다.


 원래 제가 의도하였던 코드는 JellyBean에서 표시되는 아래와 같은 화면입니다. 사진이 보이고, Action 버튼을 이용하여 공유 버튼이 표시되는 코드입니다. 줄이면, ICS 처럼 텍스트만 보이는 그런 Notification 을 원한것이죠.



동일한 코드로 Android L에서 표시하였더니 아래와 같이 표시됩니다. 조금 달라진게 눈에 보이긴 하지만 동일하게 표시됩니다.



V4 라이브러리 살펴보기

 Android 3.0 부터 Notification의 Builder가 생겼습니다. ICS에서도 이 Builder를 사용하는데 JellyBean 부터 달라진 점이라면 바로 위의 사진처럼 큰 사진을 표시하거나, 2줄 이상의 컨텐츠 내용을 표시할 수 있는 Style이 추가되었습니다. Android L 부터는 Wearable과 관련된 Notification 2가지가 추가되었습니다.

 Notification APIhttp://developer.android.com/reference/android/app/Notification.html


 이런 Style은 JellyBean 4.1인 API 16번 부터 사용이 가능하기에 ICS이하에서는 이 Style을 사용할 수 없습니다. 그래서 V4 라이브러리를 사용하면 JellyBean 4.1 이하 버전에서도 하나의 코드를 사용하여 오류 없이 적용이 가능합니다.


NotificationManager를 이용하여 Notification을 표시하게 되는데 Builder를 Notification.Builder를 사용하지 않고, V4의 NotificationCompat.Builder를 사용하면 4.1부터 적용되는 Style을 4.1 이하에서도 하나의 코드로 사용할 수 있습니다.

V4 NotificationCompact http://developer.android.com/reference/android/support/v4/app/NotificationCompat.html



V4 라이브러리 사용하기

 최신 Android SDK에서는 V4 라이브러리가 libs 폴더아래에 생성되어 있습니다. 만약 생성되지 않았다면 자신의 프로젝트 오른쪽 마우스 클릭 후 Android Tools -> Add Support Library 를 통해 쉽게 다운로드 받을 수 있습니다.


주요 예제코드

 - 해당 예제는 Progress를 업데이트 하고(파일 다운로드 처럼 예제 작성), 완료가 되면 사진을 BigPictureStyle 로 보여주는 예제입니다.

 아래 예제 코드는 BigPictureStyle을 사용하지 않고, 아래와 같이 작성하였으며, V4 라이브러리의 NotificationCompat.Builder를 사용하여 작성한 예제 코드입니다. BigPictureStyle를 바로 생성하여 사용하지 않고, setStyle를 추가로 생성하였습니다. 생성 코드 전체는 아래와 같습니다.

 해당 코드는 NotificationCompat를 Notification으로만 변경하여도 문제없이 동작하나 ICS 제한은 생기게 됩니다.

NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());

// When the loop is finished, updates the notification
// setContentText - 현재 Notification의 제목을 작성
builder.setContentText(getResources().getString(R.string.notification_content_download_complate))
                // Notification이 표시되면서 화면에 잠시 노출되는 내용.
		.setTicker(getResources().getString(R.string.notification_content_download_complate))
		.setProgress(0, 0, false) // Removes the progress bar
                // Notification을 누를 경우 자동으로 닫히도록 처리(여기서 Action을 눌렀을때는 닫히지 않습니다.)
		.setAutoCancel(true)
		.setSmallIcon(android.R.drawable.ic_menu_gallery)
                // Notification을 눌렀을 경우 처리되는 Intent
		.setContentIntent(viewPendingIntent)
                // BitPictureStyle을 적용하는 코드
		.setStyle(new NotificationCompat.BigPictureStyle()
                           // bigPicture의 이미지 파일은 Bitmap으로 처리되어야 합니다.
                           .bigPicture(photo)
                           // 화면이 펼쳐진 상태에서 보여질 아이콘을 Bitmap으로 처리해줍니다.
                           .bigLargeIcon(BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_menu_gallery))
                 // 하단의 공유 버튼
                 .addAction(android.R.drawable.ic_menu_share, getResources().getString(R.string.share), sendPendingIntent);



전체 코드.

 별도의 주석은 달지 않으며 아래와 같은 코드를 사용한 예제입니다.

public class NotificationExample2 extends GenericActivity {
	private final static String EXTERNAL_STORAGE = Environment.getExternalStorageDirectory().toString() + "/share.png";
	private final static int DELAY_DURATION = 500;
	
	private NotificationManager notifyManager;
	private NotificationCompat.Builder builder;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_notification_example1);
		
		Button btn = (Button) findViewById(R.id.start);
		btn.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
				
				if (notifyManager != null) {
					notifyManager.cancel(0);
				}
				
				builder = new NotificationCompat.Builder(getApplicationContext());
				
				// progress notification setting
				builder.setContentTitle(getResources().getString(R.string.notification_title_download))
						.setContentText(getResources().getString(R.string.notification_content_progress))
						.setTicker(getResources().getString(R.string.notification_ticker))
						.setSmallIcon(R.drawable.perm_group_sync_settings);
				
				final Bitmap photo = BitmapFactory.decodeResource(getResources(), R.drawable.notification_image);
				
				Intent intent = new Intent(Intent.ACTION_SEND);
				intent.setType("image/*");
				Uri uri = Uri.fromFile(new File(EXTERNAL_STORAGE));
				intent.putExtra(Intent.EXTRA_STREAM, uri);
				final PendingIntent sendPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
				
				Intent actionIntent = new Intent(Intent.ACTION_VIEW);
				actionIntent.setDataAndType(Uri.fromFile(new File(EXTERNAL_STORAGE)),"image/*");
				final PendingIntent viewPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, actionIntent, 0);
				
				copyDrawableImageToFileThread(R.drawable.notification_image, EXTERNAL_STORAGE);
				
				new Thread(new Runnable() {
					
					@Override
					public void run() {
						// Do the "lengthy" operation 20 times
						for (int incr = 0; incr <= 100; incr += 5) {
							// Sets the progress indicator to a max value, the current completion percentage, and "determinate" state
							builder.setProgress(100, incr, false);
							
							//Displays the progress bar for the first time.
							notifyManager.notify(0, builder.build());
							builder.setNumber(incr);
							
							// Sleep the thread, simulation an operation that takes time
							try {
								// Sleep for 500 ms
								Thread.sleep(DELAY_DURATION);
							} catch (InterruptedException e) {
								Log.e(CLASS_NAME, "sleep failure");
							}
						}
						
						// When the loop is finished, updates the notification
						builder.setContentText(getResources().getString(R.string.notification_content_download_complate))
							.setTicker(getResources().getString(R.string.notification_content_download_complate))
							.setProgress(0, 0, false) // Removes the progress bar
							.setAutoCancel(true)
							.setSmallIcon(android.R.drawable.ic_menu_gallery)
							.setContentIntent(viewPendingIntent)
							.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(photo).bigLargeIcon(BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_menu_gallery)))
							.addAction(android.R.drawable.ic_menu_share, getResources().getString(R.string.share), sendPendingIntent);
						notifyManager.notify(0, builder.build());
					}
				}).start();
			}
		});
	}
	
	private void copyDrawableImageToFileThread(final int id, final String outFileName) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				copyDrawableIamgeToFile(id, outFileName);
			}
		}).start();
	}
	
	/**
	 * Resource image to sdcard file copy
	 */
	private void copyDrawableIamgeToFile(int resourceID, String outFileName) {
		Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resourceID);
		FileOutputStream outputStream = null;
		
		try {
			outputStream = new FileOutputStream(outFileName);
			bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
			outputStream.close();
			
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}



마무리

 간단하게 V4 라이브러리를 사용한 NotificationCompat를 살펴보았습니다. 아직은 하위 호환성에 문제가 있는 만큼 모든 버전에서 오류 없이 동작하기 위함과, 같은 방식으로 동작할 수 있고, UI도 보여줄 수 있는 라이브러리를 사용하면 좀 더 안정적으로 동작하지 않을까 생각해봅니다. Notification을 구분하시고 있으셨다면 V4 라이브러리의 NotificationCompat를 사용해보시는것도 좋은 방법이라고 생각됩니다. 참고로 최신 Android SDK에서는 V4 라이브러리가 포함되어 있으며, 포함되지 않더라도, Android Tools 메뉴에서(프로젝트 오른쪽 마우스)  -> add Support Library 버튼만 눌러주시면 간단한 확인 후 다운로드가 가능하여, 빠르게 적용이 가능합니다.


 이상 NotificationCompat를 간단하게 살펴보았습니다.



댓글