Notice
Recent Posts
Recent Comments
Today
Total
03-29 01:50
Archives
관리 메뉴

Jeongchul Kim

Smart Mirror 스마트 미러 만들기- 7 앱 Application 편 본문

스마트미러

Smart Mirror 스마트 미러 만들기- 7 앱 Application 편

김 정출 2016. 5. 9. 14:46


Smart Mirror 스마트 미러 만들기- 7 소프트웨어 - 앱 Application 편



Github

https://github.com/KimJungChul/smartmirror-web-application

Youtube

https://www.youtube.com/watch?v=1jEWvWtXniQ




오랜만에 스마트 미러편으로 포스팅합니다.

앱에 대해서 많은 분들이 궁금해하셨고, 늦게 포스팅하여 죄송합니다.




APP(Application)의 주요 기능

앱의 주요 기능으로는 딱 두 가지입니다.

(스마트 미러가 와이파이로 인터넷을 접속한다면, 앱도 동일한 와이파이에 접속하셔야 하고, 또한 앱의 IP 설정도 미러의 IP로 변경하셔야 통신이 가능합니다. ) 

1. 음성 인식 STT 전송

스마트 미러의 음성 인식 API(Speech Recongizer, Annyang API)가 오후 4시 경에 되는 제한이 있고자 하여,

그 문제점을 해결하고자 스마트 폰의 앱에서 미러 대신 음성인식(Google Speech API, STT(Speech To Text) 여기서는 시간 리밋 제한이 없습니다.)을 받아 스마트 미러의 웹 서버로 명령어를 전송하는 것입니다. 웹 서버로 전송하기 위해서 미러 쪽에서는 Express를 이용한 웹 서버에서 GET으로 통신을 받아야 하며, 앱 단에서는 STT를 통해 인식 받은 Text를 비동기식 AsyncTask의 HttpURLConnection으로 연결해 값을 전송해야 합니다. 이후에 다시 언급하도록 하겠습니다.


2. 스마트 폰 Notification 전송(전화,문자,메일,카카오톡,페이스북 알림)

Android에서는 NotificationListenerService를 통해, 각종 알림의 패키지 정보와 제목과 내용을 알아낼 수 있습니다. 이를 테면 카톡에서는 보낸 사용자는 누구이며, 내용은 어떠하고, 또한 사용자의 프로필 이미지도 알아 낼 수 있죠. 그러한 정보를 스마트 미러로 보내어, 알림 받는 기능을 구현한 것입니다. 이 또한 미러에서는 GET으로 데이터를 받으며, 앱에서는 HttpURLConnection으로 데이터를 전송합니다.



APP UI


인트로 화면 Splash

Facebook 로그인 화면  




유저 정보 화면

앱의 IP설정화면
(스마트 미러IP동일 요구)



음성 인식 화면 부분(STT)
(미러 전송하는 기능)

개발자 정보 화면




APP Source

actionbarsherlock

slidingmenu_library

위의 import된 두 개의 패키지는 슬라이딩 메뉴를 위한 것입니다.

facebook

facebook api의 LoginButton 및 페이스북 프로필 정보를 가져오기 위해 사용하였습니다.

app

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

  package="softwarek.kimjungchul.smartmirror" >

  /* 인터넷 사용 퍼미션*/

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

  /* 핸드폰 정보 읽어오는 퍼미션*/

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

  /* 사용자 정보 읽어오는 퍼미션*/

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

  /* 음성 인식을 위한 permission*/

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

  <application

      android:allowBackup="true"

      android:icon="@drawable/smartmirroricon"

      android:label="@string/app_name"

      android:supportsRtl="true"

      android:theme="@style/AppTheme" >

 /* 페이스북 API 사용*/

      <meta-data

          android:name="com.facebook.sdk.ApplicationId"

          android:value="@string/facebook_app_id" /> // 페이스북 앱 개발 승인 받은 KEY를 넣어주세요

      <activity

          android:name=".SplashActivity"

          android:label="@string/app_name"

          android:theme="@style/Theme.Sherlock.NoActionBar" >

          <intent-filter>

              <action android:name="android.intent.action.MAIN" />


              <category android:name="android.intent.category.LAUNCHER" />

          </intent-filter>

      </activity>

      <activity

          android:name=".LoginActivity"

          android:label="@string/app_name"

          android:theme="@style/Theme.Sherlock.NoActionBar" />


      <activity

          android:name=".MainActivity"

          android:label="@string/app_name" >

      </activity>


      /* Notification*/

      <service

          android:name=".NotiListenService"

          android:enabled="true"

          android:exported="true"

          android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >

          <intent-filter>

              <action android:name="android.service.notification.NotificationListenerService" />

          </intent-filter>

      </service>

  </application>

</manifest>


SplashActivity.java

SharedPreference 환경 변수를 사용해 페이스북 로그인을 했는지 아닌지를 분별합니다.

로그인을 안했다면 LoginActivity, 로그인을 했다면 MainActivity로 이동합니다.

SPLASH_DISPLAY_LENGTH의 2000은 2초를 의미하며, 시간을 조정하실 수 있습니다.


package softwarek.kimjungchul.smartmirror;


import android.app.Activity;

import android.content.Intent;

import android.content.SharedPreferences;

import android.os.Bundle;

import android.os.Handler;

import android.util.Log;


public class SplashActivity extends Activity {

  /** Duration of wait **/

  private final int SPLASH_DISPLAY_LENGTH = 2000;


  /** Called when the activity is first created. */

  @Override

  public void onCreate(Bundle bundle) {

      super.onCreate(bundle);

      setContentView(R.layout.activity_splash);


      new Handler().postDelayed(new Runnable() {

          @Override

          public void run() {

              /** SharedPreference 환경 변수 사용 **/

              SharedPreferences prefs = getSharedPreferences("login",0);

              /** prefs.getString() return값이 null이라면 2번째 함수를 대입한다. **/

              String facebook_login = prefs.getString("FACEBOOK_LOGIN","LOGOUT");

              Log.d("FACEBOOK_LOGIN::::::", facebook_login);

              /** 환경 변수 중 로그인 부분을 체킹하여 Activity 전환 */

              // 페이스북 로그인

              if(facebook_login.equals("LOGIN")) {

                  Intent mainIntent = new Intent(SplashActivity.this, MainActivity.class);

                  SplashActivity.this.startActivity(mainIntent);

                  SplashActivity.this.finish();

              }

              // 페이스북 로그아웃

              else if(facebook_login.equals("LOGOUT")) {

                  Intent loginIntent = new Intent(SplashActivity.this, LoginActivity.class);

                  SplashActivity.this.startActivity(loginIntent);

                  SplashActivity.this.finish();

              }

           }

      }, SPLASH_DISPLAY_LENGTH);

  }

}


LoginActivity.java

페이스북 로그인 버튼을 만들어, 페이스북 로그인을 구현하였습니다.

자세한 사항은 페이스북 개발 Doc을 참고하시면 이해가 가실 것입니다.

사용자 디바이스 핸드폰 정보를 가져와 핸드폰 번호를 알 수 있으며,

사용자 핸드폰의 동기화된 구글 메일 주소도 가져올 수 있습니다.

자체 가입 기능을 만드신다면 참고해주세요 :D


package softwarek.kimjungchul.smartmirror;



import android.accounts.Account;

import android.accounts.AccountManager;

import android.app.Activity;

import android.content.Context;

import android.content.Intent;

import android.content.SharedPreferences;

import android.os.Bundle;

import android.telephony.TelephonyManager;

import android.util.Log;

import android.util.Patterns;

import android.widget.Toast;


import com.facebook.AccessToken;

import com.facebook.CallbackManager;

import com.facebook.FacebookCallback;

import com.facebook.FacebookException;

import com.facebook.FacebookSdk;

import com.facebook.Profile;

import com.facebook.login.LoginResult;

import com.facebook.login.widget.LoginButton;


import java.util.regex.Pattern;


public class LoginActivity extends Activity {


  private CallbackManager mCallbackManger;

  /* facebook profile */

  Profile profile;


  @Override

  protected void onCreate(Bundle savedInstanceState) {

      super.onCreate(savedInstanceState);

      FacebookSdk.sdkInitialize(getApplicationContext());

      setContentView(R.layout.activity_login);

      mCallbackManger = CallbackManager.Factory.create();


      LoginButton loginButton = (LoginButton)findViewById(R.id.login_button);


      loginButton.registerCallback(mCallbackManger, new FacebookCallback<LoginResult>() {

          @Override

          public void onSuccess(LoginResult loginResult) {

              AccessToken.setCurrentAccessToken(loginResult.getAccessToken());

              /* 페이스북 프로필을 가져온다*/

              profile = Profile.getCurrentProfile();

              String userId = profile.getId();

              String user_name = profile.getName();

              String user_password = "";



              /* 사용 디바이스 핸드폰 번호 알아오기*/

              TelephonyManager systemService = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);

              String user_phone = systemService.getLine1Number();

              user_phone = user_phone.substring(user_phone.length() - 10, user_phone.length());

              user_phone = "0" + user_phone;


              /* 안드로이드 기기 구글 동기화 이메일 주소 가져오기 */

              Pattern emailPattern = Patterns.EMAIL_ADDRESS;

              AccountManager am = (AccountManager)getSystemService(Context.ACCOUNT_SERVICE);

              Account[] accounts = am.getAccounts();

              String user_email="";

              for (Account account : accounts) {

                  if (emailPattern.matcher(account.name).matches()) {

                      user_email = account.name;

                  }

              }


              /** SharedPreferences로 Facbook 정보 저장*/

              /* 로그인될 시 세션을 유지하기 위해 환경 변수에 facebook login session 관리를 저장한다. */

              SharedPreferences prefs = getSharedPreferences("login", 0);

              SharedPreferences.Editor editor = prefs.edit();

              editor.putString("FACEBOOK_LOGIN", "LOGIN");

              editor.commit();

              /* 페이스 북 로그인이 처음이라면 SharedPreferences와 DB에 저장해야 한다.*/

              String facebookFirstLogin = prefs.getString("FACEBOOK_FIRST_LOGIN","TRUE");

              Log.d("Facebook ::::", "First Login ? " + facebookFirstLogin);

              if(facebookFirstLogin.equals("TRUE")) {

                  /** SharedPreferences 저장*/

                  editor.putString("FACEBOOK_FIRST_LOGIN","FALSE");

                  editor.putString("USER_ID", userId);

                  editor.putString("USER_NAME", user_name);

                  editor.putString("USER_PASSWORD", user_password);

                  editor.putString("USER_PHONE", user_phone);

                  editor.putString("USER_EMAIL", user_email);

                  editor.commit();


                  Log.d("FaceBook :::", "id : " + userId);

                  Log.d("FaceBook :::", "name : " + user_name);

                  Log.d("FaceBook :::", "phone : " + user_phone);

                  Log.d("FaceBook :::", "email : " + user_email);


              }

              Intent mainIntent = new Intent(LoginActivity.this, MainActivity.class);

              startActivity(mainIntent);

              finish();

          }


          @Override

          public void onCancel() {

              Toast.makeText(LoginActivity.this, "페이스북 로그인이 취소 되었습니다.", Toast.LENGTH_SHORT).show();

          }


          @Override

          public void onError(FacebookException error) {

              Toast.makeText(LoginActivity.this, "페이스북 로그인이 취소 되었습니다.", Toast.LENGTH_SHORT).show();

          }

      });

  }


  @Override

  public void onActivityResult(int requestCode, int resultCode, Intent data) {

      super.onActivityResult(requestCode, resultCode, data);

      mCallbackManger.onActivityResult(requestCode, resultCode, data);

  }

}


NotiListenService.java

핵심 기능 중에 한 부분으로서, 스마트 미러로 Notification 알림을 전송하는 부분입니다.

사용자가 앱에 설정해놓은 ip주소를 통해 데이터를 보내며,

여기서의 데이터는 알람으로 온 앱에 대한 패키지 이름과, 제목, 내용입니다. 문자열이며

앱 단에서 메일, 전화, 문자, 카카오톡, 페이스북 패키지 이름과 비교하여, 해당하는 것만 전송합니다.

HttpURLConnection을 이용해 스마트 미러로 GET통신을 이용해 전송합니다.

package softwarek.kimjungchul.smartmirror;


import android.app.Notification;

import android.content.SharedPreferences;

import android.os.Bundle;

import android.service.notification.NotificationListenerService;

import android.service.notification.StatusBarNotification;

import android.util.Log;


import java.io.BufferedReader;

import java.io.InputStreamReader;

import java.net.HttpURLConnection;

import java.net.URL;

import java.net.URLEncoder;


public class NotiListenService extends NotificationListenerService {


  private String smartmirror_ip; // 스마트 미러의 할당받은 ip 주소


  /* 환경 변수 */

  private SharedPreferences prefs;


  public NotiListenService() {

  }


  @Override

  public void onNotificationPosted(StatusBarNotification sbn) {

      //super.onNotificationPosted(sbn);

      /**

       * 보안 / 알림 액세스 / 알림 설정해야함!

       */

      Notification noti = sbn.getNotification();

      Bundle bundle = noti.extras;


      prefs = getSharedPreferences("login", 0);

      smartmirror_ip = prefs.getString("SMARTMIRROR_IP", "0.0.0.0");

      Log.d("smartmirror IP : ",smartmirror_ip);


      /*

      int icon = bundle.getInt(Notification.EXTRA_SMALL_ICON);

      Bitmap notificationLargeIcon = ((Bitmap) bundle.getParcelable(Notification.EXTRA_LARGE_ICON));

      */


      /**

       * strPacakge : 패캐지 이름

       * title : 이름, 사용자, 전화번호 등...

       * text : 내용 */

      String strPackage = sbn.getPackageName();

      String title = bundle.getString(Notification.EXTRA_TITLE);

      CharSequence text = bundle.getCharSequence(Notification.EXTRA_TEXT);


      if(title == null) title = "";

      if(text == null) text = "";


      Log.d("[noti] get :","package : "+strPackage+" // title : "+title+" // text : "+text);


      /* 페이스북, 구글 메일, 카카오톡, 전화, 문자 패키지만 라즈베리파이 알림으로 전송 */

      if(strPackage.equals("com.facebook.orca") || strPackage.equals("com.facebook.katana") || strPackage.equals("com.android.mms")

              || strPackage.equals("com.kakao.talk") || strPackage.equals("com.google.android.gm") || strPackage.equals("com.lge.ltecall")) {


          /** 라즈베리 파이2 전송하는 부분 */

          try {

              String enPackage = URLEncoder.encode(strPackage, "UTF-8");

              String enTitle = URLEncoder.encode(title, "UTF-8");

              String enText = URLEncoder.encode(text.toString(), "UTF-8");

          /* 라즈베리 파이 IP : 9090 (port) */

              String urlRasp = "http://"+smartmirror_ip+":9090/noti.do?package=" + enPackage + "&title=" + enTitle + "&text=" + enText;

              Log.d("smartmirror url : ",urlRasp);

              URL url = new URL(urlRasp);

              HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();


              BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));


              String response = null;


              while (true) {

                  response = br.readLine();

                  if (response == null) break;

                  Log.d("[noti] response :", response);

              }


              urlConnection.disconnect();


          } catch (Exception e) {

              e.printStackTrace();

          }

      }

  }


  @Override

  public void onNotificationRemoved(StatusBarNotification sbn) {

      //super.onNotificationRemoved(sbn);

  }

}




MainActivity.java

메인 액티비티에서는 전반적인 슬라이딩 메뉴에 대한 구성을 만들어
각각의 메뉴에 대한 Fragment를 호출하는 부분입니다.


package softwarek.kimjungchul.smartmirror;


import android.app.ActionBar;

import android.content.res.Configuration;

import android.content.res.TypedArray;

import android.os.Bundle;

import android.support.v4.app.ActionBarDrawerToggle;

import android.support.v4.app.FragmentTransaction;

import android.support.v4.widget.DrawerLayout;

import android.view.View;

import android.widget.AdapterView;

import android.widget.ListView;


import com.actionbarsherlock.app.SherlockFragmentActivity;

import com.actionbarsherlock.view.MenuItem;

import com.facebook.FacebookSdk;


import java.util.ArrayList;


import softwarek.kimjungchul.smartmirror.Adapter.NavDrawerItem;

import softwarek.kimjungchul.smartmirror.Adapter.NavDrawerListAdapter;


public class MainActivity extends SherlockFragmentActivity {


  private DrawerLayout mDrawerLayout;

  private ListView mDrawerList;

  private ActionBarDrawerToggle mDrawerToggle;


  // nav drawer title

  private CharSequence mDrawerTitle;


  // used to store app title

  private CharSequence mTitle;


  // slide menu items

  private String[] navMenuTitles;

  private TypedArray navMenuIcons;


  private ArrayList<NavDrawerItem> navDrawerItems;

  private NavDrawerListAdapter adapter;



  @Override

  protected void onCreate(Bundle savedInstanceState) {

      super.onCreate(savedInstanceState);

      setContentView(R.layout.activity_main);


      FacebookSdk.sdkInitialize(getApplicationContext());


      mTitle = mDrawerTitle = getTitle();


      // load slide menu items

      navMenuTitles = getResources().getStringArray(R.array.nav_drawer_items);


      // nav drawer icons from resources

      navMenuIcons = getResources()

              .obtainTypedArray(R.array.nav_drawer_icons);


      mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);

      mDrawerList = (ListView) findViewById(R.id.list_slidermenu);


      navDrawerItems = new ArrayList<NavDrawerItem>();


      // adding nav drawer items to array

      /* 개인 유저 정보*/

      navDrawerItems.add(new NavDrawerItem(navMenuTitles[0],navMenuIcons.getResourceId(0, -1) ));

      /* 미러 음성 전송 */

      navDrawerItems.add(new NavDrawerItem(navMenuTitles[1], navMenuIcons.getResourceId(1, -1)));

      /* 미러 IP 설정 */

      navDrawerItems.add(new NavDrawerItem(navMenuTitles[2], navMenuIcons.getResourceId(2, -1)));

      /* 스마트 미러 정보 */

      navDrawerItems.add(new NavDrawerItem(navMenuTitles[3], navMenuIcons.getResourceId(3, -1)));


      // Recycle the typed array

      navMenuIcons.recycle();


      mDrawerList.setOnItemClickListener(new SlideMenuClickListener());


      // setting the nav drawer list adapter

      adapter = new NavDrawerListAdapter(getApplicationContext(),

              navDrawerItems);

      mDrawerList.setAdapter(adapter);


      // enabling action bar app icon and behaving it as toggle button

      ActionBar actionBar = getActionBar();

      getActionBar().setDisplayHomeAsUpEnabled(true);

      getActionBar().setHomeButtonEnabled(true);


      mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,

              R.drawable.apptheme_ic_navigation_drawer, //nav menu toggle icon

              R.string.app_name, // nav drawer open

              R.string.app_name // nav drawer close

      ) {

          public void onDrawerClosed(View view) {

              getActionBar().setTitle(mTitle);

              // calling onPrepareOptionsMenu() to show action bar icons

              invalidateOptionsMenu();

          }


          public void onDrawerOpened(View drawerView) {

              getActionBar().setTitle(mDrawerTitle);

              // calling onPrepareOptionsMenu() to hide action bar icons

              invalidateOptionsMenu();

          }

      };

      mDrawerLayout.setDrawerListener(mDrawerToggle);


      if (savedInstanceState == null) {

          // on first time display view for first nav item

          displayView(1);

      }

  }



  /**

   * 슬라이드 메뉴 클릭 리스너

   */

  private class SlideMenuClickListener implements ListView.OnItemClickListener {

      @Override

      public void onItemClick(AdapterView<?> parent, View view, int position,

                              long id) {

          // display view for selected nav drawer item

          displayView(position);

      }

  }


  /** 슬라이딩 메뉴 오픈 / 클로즈 */

  @Override

  public boolean onOptionsItemSelected(MenuItem item) {

      FragmentTransaction ft = getSupportFragmentManager().beginTransaction();

      switch (item.getItemId()) {

          case android.R.id.home:

              if (mDrawerLayout.isDrawerOpen(mDrawerList)) {

                  mDrawerLayout.closeDrawer(mDrawerList);

              } else {

                  mDrawerLayout.openDrawer(mDrawerList);

              }

              break;

          default:

              return super.onOptionsItemSelected(item);

      }

      return true;

  }


  /**

   * Diplaying fragment view for selected nav drawer list item

   * 슬라이딩 메뉴를 프래그먼트와 연동

   * */

  private void displayView(int position) {

      // update the main content by replacing fragments

      FragmentTransaction ft = getSupportFragmentManager().beginTransaction();

      switch (position) {

          case 0:

              ft.replace(R.id.frame_container,new HomeFragment());

              break;

          case 1:

              ft.replace(R.id.frame_container,new SetIPFragment());

              break;

          case 2:

              ft.replace(R.id.frame_container,new SendSpeechCommandFragment());

              break;

          case 3:

              ft.replace(R.id.frame_container,new SmartMirrorFragment());

              break;

          default:

              break;


      }

      ft.commit();

      mDrawerList.setItemChecked(position, true);

      mDrawerLayout.closeDrawer(mDrawerList);

  }


  @Override

  public void setTitle(CharSequence title) {

      mTitle = title;

      getActionBar().setTitle(mTitle);

  }


  /**

   * When using the ActionBarDrawerToggle, you must call it during

   * onPostCreate() and onConfigurationChanged()...

   */

  @Override

  protected void onPostCreate(Bundle savedInstanceState) {

      super.onPostCreate(savedInstanceState);

      // Sync the toggle state after onRestoreInstanceState has occurred.

      mDrawerToggle.syncState();

  }

  @Override

  public void onConfigurationChanged(Configuration newConfig) {

      super.onConfigurationChanged(newConfig);

      // Pass any configuration change to the drawer toggls

      mDrawerToggle.onConfigurationChanged(newConfig);

  }

}


HomeFragment.java

페이스북 로그인을 한 유저의 정보를 살펴볼 수 있습니다.

또한 로그아웃이 가능하도록 버튼을 구성하였습니다.


package softwarek.kimjungchul.smartmirror;


import android.app.ProgressDialog;

import android.content.Intent;

import android.content.SharedPreferences;

import android.os.Bundle;

import android.os.Handler;

import android.support.v4.app.Fragment;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.Button;

import android.widget.TextView;


import com.facebook.FacebookSdk;

import com.facebook.Profile;

import com.facebook.login.LoginManager;

import com.facebook.login.widget.ProfilePictureView;


public class HomeFragment extends Fragment

{

  private Profile profile;

  private TextView tv_home_user_id;

  private TextView tv_home_user_name;

  private ProfilePictureView profilePictureView;


  private ProgressDialog mProgressDialog;


  @Override

  public void onCreate(Bundle instance)

  {

      super.onCreate(instance);


  }


  @Override

  public View onCreateView(LayoutInflater inflater, ViewGroup container,

                           Bundle savedInstanceState) {

      View view = inflater.inflate(R.layout.fragment_home, container, false);


      FacebookSdk.sdkInitialize(view.getContext());

      profile = Profile.getCurrentProfile();


      mProgressDialog = new ProgressDialog(getActivity());

      mProgressDialog.setMessage("Log out ...");


      String id = profile.getId();

      String name = profile.getName();


      tv_home_user_id = (TextView)view.findViewById(R.id.home_user_id);

      tv_home_user_name = (TextView)view.findViewById(R.id.home_user_name);


      tv_home_user_id.setText(id);

      tv_home_user_name.setText(name);


      profilePictureView = (ProfilePictureView) view.findViewById(R.id.home_profile_image_facebook);

      profilePictureView.setProfileId(id);


      /** Facebook logout*/

      Button logoutButton = (Button) view.findViewById(R.id.logout_button);

      logoutButton.setOnClickListener(new View.OnClickListener() {

          @Override

          public void onClick(View v) {

              Handler handler = new Handler();


              mProgressDialog.setCancelable(false);

              mProgressDialog.show();


              handler.postDelayed(new Runnable() {

                  @Override

                  public void run() {

                       /* -- switch -> Button R.id. */

                      LoginManager.getInstance().logOut();

                      SharedPreferences prefs = getActivity().getSharedPreferences("login", 0);

                      SharedPreferences.Editor editor = prefs.edit();

                      editor.putString("FACEBOOK_LOGIN", "LOGOUT");

                      editor.commit();


                      Intent mainIntent = new Intent(getActivity(), SplashActivity.class);

                      startActivity(mainIntent);

                      getActivity().finish();

                      if (mProgressDialog != null && mProgressDialog.isShowing()) {

                          mProgressDialog.dismiss();

                      }

                  }

              }, 1000);

          }

      });

      return view;

  }

}


SendSpeechCommandFragment

이어서 핵심 기능 중 STT를 이용해 음성인식을 통해 사용자의 음성을 텍스트로 변환해 스마트 미러로 전송하는 부분입니다. 정말 힘들게 자료 수집하고 구현한 부분입니다.

음성인식 버튼을 누르면, 무한 Loop 돌 듯이 계속 사용자의 음성 인식을 시작합니다.

음성으로 ‘정지’ 또는 음성 명령 정지 버튼을 통해 음성 인식은 정지됩니다.

음성 인식 받은 데이터는 HttpURLConnection으로 전송하며, Notification 전송과 동일합니다.

package softwarek.kimjungchul.smartmirror;


import android.content.Intent;

import android.content.SharedPreferences;

import android.os.AsyncTask;

import android.os.Bundle;

import android.speech.RecognitionListener;

import android.speech.RecognizerIntent;

import android.speech.SpeechRecognizer;

import android.util.Log;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.Button;

import android.widget.ImageButton;

import android.widget.TextView;

import android.widget.Toast;


import com.actionbarsherlock.app.SherlockFragment;


import java.io.BufferedReader;

import java.io.InputStreamReader;

import java.io.UnsupportedEncodingException;

import java.net.HttpURLConnection;

import java.net.URL;

import java.net.URLEncoder;


public class SendSpeechCommandFragment extends SherlockFragment{


  private Intent i;

  private TextView tv_speech;

  private ImageButton btn_userSpeech;

  private Button btn_stopRecog;


  private SpeechRecognizer mRecognizer;


  private String input_sst; // 명령 결과

  private String enCommand;


  private String smartmirror_ip; // 스마트 미러의 할당받은 ip 주소


  /* 환경 변수 */

  private SharedPreferences prefs;


  /* 스마트 미러로 네트워크 전송 부분*/

  SendComandTask sendComandTask;


  @Override

  public void onCreate(Bundle savedInstanceState) {

      super.onCreate(savedInstanceState);

  }


  @Override

  public View onCreateView(LayoutInflater inflater, ViewGroup container,

                           Bundle savedInstanceState) {


      View rootView = inflater.inflate(R.layout.fragment_sendspeechcommend, container, false);


      /* 레이아웃의 컴포넌트를 가져옵니다.*/

      tv_speech = (TextView) rootView.findViewById(R.id.tv_speech);

      btn_userSpeech = (ImageButton) rootView.findViewById(R.id.userSpeech);

      btn_stopRecog = (Button) rootView.findViewById(R.id.btn_stopRecog);


      i = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); // Intent 생성

      i.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,getActivity().getPackageName()); // 호출한 패키지

      i.putExtra(RecognizerIntent.EXTRA_LANGUAGE,"ko-KR"); // 인식한 언어 설정

      i.putExtra(RecognizerIntent.EXTRA_PROMPT, "말해주세요"); // 유저에게 보여줄 문자


      /* 환경 변수 */

      prefs = getActivity().getSharedPreferences("login", 0);

      smartmirror_ip = prefs.getString("SMARTMIRROR_IP", "0.0.0.0");




      btn_userSpeech.setOnClickListener(new View.OnClickListener() {

          @Override

          public void onClick(View v) {

              if (v.getId() == R.id.userSpeech) {

                  mRecognizer = SpeechRecognizer.createSpeechRecognizer(getActivity());

                  mRecognizer.setRecognitionListener(listener);

                  i = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); // Intent 생성

                  i.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, getActivity().getPackageName()); // 호출한 패키지

                  i.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "ko-KR"); // 인식한 언어 설정

                  i.putExtra(RecognizerIntent.EXTRA_PROMPT, "말해주세요"); // 유저에게 보여줄 문자

                  mRecognizer.startListening(i);

              }

          }

      });


      btn_stopRecog.setOnClickListener(new View.OnClickListener() {

          @Override

          public void onClick(View view) {

              mRecognizer.destroy();

          }

      });


      return rootView;

  }


  private RecognitionListener listener = new RecognitionListener() {

      /** 음성인식 준비 완료 */

      @Override

      public void onReadyForSpeech(Bundle params) {

          //Toast.makeText(getActivity(),"SST Ready",Toast.LENGTH_SHORT).show();

      }


      /** 음성인식 시작 */

      @Override

      public void onBeginningOfSpeech() {

          Toast.makeText(getActivity(),"음성 인식 시작",Toast.LENGTH_SHORT).show();

      }


      /** 입력 소리 변경 시 */

      @Override

      public void onRmsChanged(float rmsdB) {

      }


      /** 더 많은 소리를 받을 경우 */

      @Override

      public void onBufferReceived(byte[] buffer) {

      }


      /** 음성인식 끝남 */

      @Override

      public void onEndOfSpeech() {

          //Toast.makeText(getActivity(),"SST Finish",Toast.LENGTH_SHORT).show();

      }


      /** 에러 발생 */

      @Override

      public void onError(int error) {

          //Toast.makeText(getActivity(),"SST Retry :D",Toast.LENGTH_SHORT).show();

          mRecognizer.destroy();

          mRecognizer = SpeechRecognizer.createSpeechRecognizer(getActivity());

          mRecognizer.setRecognitionListener(listener);

          mRecognizer.startListening(i);

      }


      /** 음성인식 결과 받음 */

      @Override

      public void onResults(Bundle results) {

          String key = "";

          key = SpeechRecognizer.RESULTS_RECOGNITION;

          input_sst = results.getStringArrayList(key).get(0);

          if(input_sst.equals("정지")) {

              Toast.makeText(getActivity(),"음성 명령이 정지 되었습니다",Toast.LENGTH_LONG).show();

              mRecognizer.destroy();

              input_sst = "";

              return;

          }

          tv_speech.setText("" + input_sst);

          Toast.makeText(getActivity(),"말씀하신 명령 : "+input_sst,Toast.LENGTH_SHORT).show();

          Log.d("SST : ", input_sst);


          try {

              enCommand = URLEncoder.encode(input_sst, "UTF-8");

              /* AsyncTask*/

              sendComandTask = new SendComandTask();

              sendComandTask.execute();

          } catch (UnsupportedEncodingException e) {

              e.printStackTrace();

          }


          mRecognizer.destroy();


          mRecognizer = SpeechRecognizer.createSpeechRecognizer(getActivity());

          mRecognizer.setRecognitionListener(listener);

          mRecognizer.startListening(i);

      }


      /** 음성인식 결과 일부 유효 */

      @Override

      public void onPartialResults(Bundle partialResults) {

          String key = "";

          key = SpeechRecognizer.RESULTS_RECOGNITION;

          String input_sst = partialResults.getStringArrayList(key).get(0);

          tv_speech.setText("" + input_sst);

          Toast.makeText(getActivity(),"You said : "+input_sst,Toast.LENGTH_SHORT).show();

      }


      @Override

      public void onEvent(int eventType, Bundle params) {


      }

  };


  class SendComandTask extends AsyncTask<Void, Void, Void> {

      /**

       * Parameter

       *  - 1 parmas는 입력

       *  - 2 progress

       *  - 3 result는 리턴 값 */


      @Override

      protected void onPreExecute() {

          /* Thread의 Run과 같은 기능, Thread로 동작 */

          super.onPreExecute();

      }


      @Override

      protected Void doInBackground(Void... voids) {

          /** 라즈베리 파이2 전송하는 부분 */

          try {


              /* 라즈베리 파이 IP : 9090 (port) */

              String urlRasp = "http://"+smartmirror_ip+":9090/android.do?command="+enCommand;

              Log.d("smartmirror url : ",urlRasp);

              URL url = new URL(urlRasp);

              HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();


              BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));


              String response = null;


              while (true) {

                  response = br.readLine();

                  if (response == null) break;

                  Log.d("[android command]","Response : "+response);

              }


              urlConnection.disconnect();


          } catch (Exception e) {

              e.printStackTrace();

          }


          return null;

      }


      @Override

      protected void onPostExecute(Void aVoid) {

          super.onPostExecute(aVoid);

      }

  }


}


SetIPFragment.java

앱에서 스마트 미러의 ip를 동일시 하기 위한 기능입니다.

사용자는 스마트 미러를 동작하면 명령 메뉴 부분에서 ip를 확인할 수 있으며,

앱을 이용할 경우 이 Fragment에서 ip를 설정해주어야 합니다.

192.168.0.42 와 같이 XXX.XXX.XXX.XXX으로 작성해야 됩니다.

ip가 다르다면 웹서버로 데이터 전송이나 통신이 안됩니다.


package softwarek.kimjungchul.smartmirror;


import android.content.SharedPreferences;

import android.os.Bundle;

import android.support.v4.app.Fragment;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.Button;

import android.widget.EditText;

import android.widget.TextView;

import android.widget.Toast;


public class SetIPFragment extends Fragment {



  private EditText et_ip;

  private TextView tv_currentIP;

  private Button btn_setIP;


  private String current_ip; // 환경변수에 저장된 ip주소

  private String smartmirror_ip; // 스마트 미러의 할당받은 ip 주소


  /* 환경 변수 */

  private SharedPreferences prefs;

  private SharedPreferences.Editor editor;


  @Override

  public View onCreateView(LayoutInflater inflater, ViewGroup container,

                           Bundle savedInstanceState) {


      View rootView = inflater.inflate(R.layout.fragment_setip, container, false);


      tv_currentIP = (TextView)rootView.findViewById(R.id.tv_currentIP);

      et_ip = (EditText)rootView.findViewById(R.id.et_ip);

      btn_setIP = (Button) rootView.findViewById(R.id.btn_setIP);


      prefs = getActivity().getSharedPreferences("login", 0);

      editor = prefs.edit();


      current_ip = prefs.getString("SMARTMIRROR_IP","0.0.0.0");

      tv_currentIP.setText(current_ip);


      btn_setIP.setOnClickListener(new View.OnClickListener(){

          @Override

          public void onClick(View view) {

              smartmirror_ip = et_ip.getText().toString();

              editor.putString("SMARTMIRROR_IP",smartmirror_ip);

              editor.commit();

              Toast.makeText(getActivity(),"IP가"+ smartmirror_ip +"로 설정되었습니다.", Toast.LENGTH_SHORT).show();

          }

      });



      return rootView;

  }

}


SmartMirrorFragment.java

스마트 미러 개발자에 대한 내용입니다.


package softwarek.kimjungchul.smartmirror;


import android.os.Bundle;

import android.support.v4.app.Fragment;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;


public class SmartMirrorFragment extends Fragment{


  @Override

  public View onCreateView(LayoutInflater inflater, ViewGroup container,

                           Bundle savedInstanceState) {


      View rootView = inflater.inflate(R.layout.fragment_smartmirror, container, false);


      return rootView;

  }

}



레이아웃에 XML 파일에 대한 설명은 Github에 올라가 있는 소스를 참고해 주시면 감사하겠습니다.

이상 앱에 대한 포스팅을 마치겠습니다. 감사합니다 :D





Comments