/**
* Copyright 2014 Daum Kakao Corp.
*
* Redistribution and modification in source or binary forms are not permitted without specific prior written permission.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kakao;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.json.JSONArray;
import org.json.JSONException;
import com.kakao.internal.Action;
import com.kakao.internal.KakaoTalkLinkProtocol;
import com.kakao.internal.LinkObject;
/**
* 카카오톡으로 전송할 하나의 메시지를 구성해주는 Builder이다.
* 이를 이용하여 여러가지 타입의 content를 구성한다.
*/
public class KakaoTalkLinkMessageBuilder {
private final String appKey;
private final String appVer;
private final String extra;
private final AtomicInteger textType;
private final AtomicInteger imageType;
private final AtomicInteger buttonType;
private final AtomicInteger linkType;
private final List<LinkObject> linkObjList;
KakaoTalkLinkMessageBuilder(final String appKey, final String appVer, final String extra) {
this.appKey = appKey;
this.appVer = appVer;
this.extra = extra;
this.textType = new AtomicInteger(0);
this.imageType = new AtomicInteger(0);
this.buttonType = new AtomicInteger(0);
this.linkType = new AtomicInteger(0);
this.linkObjList = new ArrayList<LinkObject>();
}
/**
* 텍스트를 메시지 추가한다. 텍스트 글자수는 최대 1000자로 제한된다.
*
* @param text 추가할 메시지
* @throws KakaoParameterException 이미 텍스트 형식의 메시지가 포함되어 있는 경우 발생한다.
*/
public KakaoTalkLinkMessageBuilder addText(final String text) throws KakaoParameterException {
if (textType.getAndIncrement() == 1)
throw new KakaoParameterException(KakaoParameterException.ERROR_CODE.DUPLICATE_OBJECTS_USED,
"textType already added. each type can be added once, at most.");
final LinkObject textLink = LinkObject.newText(text);
linkObjList.add(textLink);
return this;
}
/**
* 이미지를 메시지에 추가한다. 최소 70px * 70px 지원하므로 이보다 크게 설정해야하고, 용량은 500kb 이하를 지원하므로 이보다 적은 용량의 이미지 url을 설정하도록 한다.
*
* @param src 추가할 이미지 파일이 존재하는 url
* @param width 보여줄 이미지의 가로 크기
* @param height 보여줄 이미지의 세로 크기
* @throws KakaoParameterException 이미 이미지 형식의 메시지가 포함되어 있는 경우 발생한다.
*/
public KakaoTalkLinkMessageBuilder addImage(final String src, final int width, final int height) throws KakaoParameterException {
if (imageType.getAndIncrement() == 1)
throw new KakaoParameterException(KakaoParameterException.ERROR_CODE.DUPLICATE_OBJECTS_USED,
"imageType already added. each type can be added once, at most.");
final LinkObject imageLink = LinkObject.newImage(src, width, height);
linkObjList.add(imageLink);
return this;
}
/**
* 앱으로 연결할 버튼을 메시지에 추가한다.
* 버튼 클릭시 kakao[appkey]://kakaolink으로 이동한다.
* 버튼 클릭시 앱연결 url에 append할 parameter가 있는 경우는 {@link #addAppButton(String, com.kakao.internal.Action)}을 사용한다.
* @param text 버튼에 보일 텍스트
* @throws KakaoParameterException 이미 버튼 형식의 메시지가 포함되어 있는 경우 발생한다.
*/
public KakaoTalkLinkMessageBuilder addAppButton(final String text) throws KakaoParameterException {
addAppButton(text, Action.newActionApp(null));
return this;
}
/**
* 앱으로 연결할 버튼을 메시지에 추가한다.
* 버튼 클릭시 앱연결 url에 append할 parameter가 있는 경우 사용한다.
* 버튼 클릭시 kakao[appkey]://akaolink으로 이동 하려면{@link #addAppButton(String)}를 사용한다.
* @param text 버튼에 보일 텍스트
* @param appAction app 연결 url에 append할 parameter를 os, device별로 지정
* @throws KakaoParameterException 이미 버튼 형식의 메시지가 포함되어 있는 경우 발생한다.
*/
public KakaoTalkLinkMessageBuilder addAppButton(final String text, final Action appAction) throws KakaoParameterException {
if (buttonType.getAndIncrement() == 1)
throw new KakaoParameterException(KakaoParameterException.ERROR_CODE.DUPLICATE_OBJECTS_USED,
"buttonType already added. each type can be added once, at most.");
final LinkObject appButton = LinkObject.newButton(text, appAction);
linkObjList.add(appButton);
return this;
}
/**
* 앱등록시 등록한 사이트 도메인으로 연결할 버튼을 메시지에 추가한다.
* 등록한 사이트 도메인에 path, parameter등을 더 추가 한 url로 연결할 버튼을 추가하려면 {@link #addWebButton(String, String)}을 사용한다.
* @param text 버튼에 보일 텍스트
* @throws KakaoParameterException 이미 버튼 형식의 메시지가 포함되어 있는 경우 발생한다.
*/
public KakaoTalkLinkMessageBuilder addWebButton(final String text) throws KakaoParameterException {
addWebButton(text, null);
return this;
}
/**
* 웹사이트로 연결할 버튼을 메시지에 추가한다.
* 앱등록시 등록한 사이트 도메인으로 연결할 버튼을 추가하려면 {@link #addWebButton(String)}을 사용한다.
* @param text 버튼에 보일 텍스트
* @param url 등록한 사이트 도메인에 path, parameter등을 더 추가 한 url
* @throws KakaoParameterException 이미 버튼 형식의 메시지가 포함되어 있는 경우 발생한다.
*/
public KakaoTalkLinkMessageBuilder addWebButton(final String text, final String url) throws KakaoParameterException {
if (buttonType.getAndIncrement() == 1)
throw new KakaoParameterException(KakaoParameterException.ERROR_CODE.DUPLICATE_OBJECTS_USED,
"buttonType already added. each type can be added once, at most.");
final Action webAction = Action.newActionWeb(url);
final LinkObject webButton = LinkObject.newButton(text, webAction);
linkObjList.add(webButton);
return this;
}
/**
* 앱으로 연결할 링크를 메시지에 추가한다.
* 링크 클릭시 kakao[appkey]://exec으로 이동한다.
* 앱연결 url에 append할 parameter가 있는 경우 {@link #addAppLink(String, com.kakao.internal.Action)}를 사용한다.
* @param text 링크에 보여줄 텍스트
* @throws KakaoParameterException 이미 링크 형식의 메시지가 포함되어 있는 경우 발생한다.
*/
public KakaoTalkLinkMessageBuilder addAppLink(final String text) throws KakaoParameterException {
addAppLink(text, Action.newActionApp(null));
return this;
}
/**
* 앱으로 연결할 링크를 메시지에 추가한다.
* 링크 클릭시 앱 연결 url에 append할 parameter가 있는 경우 사용한다.
* 링크 클릭시 kakao[appkey]://exec으로 이동하려면 {@link #addAppLink(String)}를 사용한다.
* @param text 링크에 보여줄 텍스트
* @param appAction 앱 연결 url에 append할 parameter를 os, device별로 지정
* @throws KakaoParameterException 이미 링크 형식의 메시지가 포함되어 있는 경우 발생한다.
*/
public KakaoTalkLinkMessageBuilder addAppLink(final String text, final Action appAction) throws KakaoParameterException {
if (linkType.getAndIncrement() == 1)
throw new KakaoParameterException(KakaoParameterException.ERROR_CODE.DUPLICATE_OBJECTS_USED,
"linkType already added. each type can be added once, at most.");
final LinkObject appLink = LinkObject.newLink(text, appAction);
linkObjList.add(appLink);
return this;
}
/**
* 앱등록시 등록한 사이트 도메인으로 연결할 링크를 메시지에 추가한다.
* 등록한 사이트 도메인에 path, parameter등을 더 추가 한 url로 연결한 링크을 추가하려면 {@link #addWebLink(String, String)}을 사용한다.
* @param text 링크에 보여줄 텍스트
* @throws KakaoParameterException 이미 링크 형식의 메시지가 포함되어 있는 경우 발생한다.
*/
public KakaoTalkLinkMessageBuilder addWebLink(final String text) throws KakaoParameterException {
addWebLink(text, null);
return this;
}
/**
* 웹사이트로 연결할 링크를 메시지에 추가한다.
* 앱등록시 등록한 사이트 도메인으로 연결할 링크을 추가하려면 {@link #addWebLink(String)}을 사용한다.
* @param text 링크에 보여줄 텍스트
* @param url 등록한 사이트 도메인에 path, parameter등을 더 추가 한 url
* @throws KakaoParameterException 이미 링크 형식의 메시지가 포함되어 있는 경우 발생한다.
*/
public KakaoTalkLinkMessageBuilder addWebLink(final String text, final String url) throws KakaoParameterException {
if (linkType.getAndIncrement() == 1)
throw new KakaoParameterException(KakaoParameterException.ERROR_CODE.DUPLICATE_OBJECTS_USED,
"linkType already added. each type can be added once, at most.");
final Action webAction = Action.newActionWeb(url);
final LinkObject webLink = LinkObject.newLink(text, webAction);
linkObjList.add(webLink);
return this;
}
/**
* 지금까지 추가된 메시지를 가지고 최종적으로 카카오톡으로 전송된 메시지를 구성한다.
* @return 카카오톡으로 전송될 최종 메시지
* @throws KakaoParameterException 추가된 메시지가 전혀 없는 경우 발생한다.
*/
public String build() throws KakaoParameterException {
try {
if (linkObjList.isEmpty())
throw new KakaoParameterException(KakaoParameterException.ERROR_CODE.CORE_PARAMETER_MISSING,
"call addAppLink or addWebLink or addAppButton or addWebButton or addText or addImage before calling build().");
final StringBuilder talkLinkURL = new StringBuilder(KakaoTalkLinkProtocol.KAKAO_TALK_LINK_URL).append("?");
talkLinkURL.append(KakaoTalkLinkProtocol.LINK_VER).append("=").append(URLEncoder.encode(KakaoTalkLinkProtocol.LINK_VERSION, KakaoTalkLinkProtocol.ENCODING)).append("&");
talkLinkURL.append(KakaoTalkLinkProtocol.API_VER).append("=").append(URLEncoder.encode(KakaoTalkLinkProtocol.API_VERSION, KakaoTalkLinkProtocol.ENCODING)).append("&");
talkLinkURL.append(KakaoTalkLinkProtocol.APP_KEY).append("=").append(URLEncoder.encode(appKey, KakaoTalkLinkProtocol.ENCODING)).append("&");
talkLinkURL.append(KakaoTalkLinkProtocol.APP_VER).append("=").append(URLEncoder.encode(appVer, KakaoTalkLinkProtocol.ENCODING)).append("&");
talkLinkURL.append(KakaoTalkLinkProtocol.EXTRAS).append("=").append(URLEncoder.encode(extra, KakaoTalkLinkProtocol.ENCODING)).append("&");
talkLinkURL.append(KakaoTalkLinkProtocol.OBJS).append("=");
final JSONArray jsonArray = new JSONArray();
for (LinkObject linkObject : linkObjList) {
jsonArray.put(linkObject.createJSONObject());
}
final String encodedValue = URLEncoder.encode(jsonArray.toString(), KakaoTalkLinkProtocol.ENCODING);
return talkLinkURL.append(encodedValue).toString();
} catch (UnsupportedEncodingException e) {
throw new KakaoParameterException(KakaoParameterException.ERROR_CODE.UNSUPPORTED_ENCODING, e);
} catch (JSONException e) {
throw new KakaoParameterException(KakaoParameterException.ERROR_CODE.JSON_PARSING_ERROR, e);
}
}
}