package com.example.cosmosclient.services; import android.Manifest; import android.app.IntentService; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Intent; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Color; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.provider.Settings; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.support.v4.app.ActivityCompat; import android.util.Log; import android.widget.Toast; import com.example.cosmosclient.R; import com.example.cosmosclient.app.Cosmos; import com.example.cosmosclient.entities.AreaInformation; import com.example.cosmosclient.entities.CosmosLocation; import com.example.cosmosclient.entities.Feature; import com.example.cosmosclient.entities.Group; import com.example.cosmosclient.entities.GroupListResponse; import com.example.cosmosclient.entities.RequestList; import com.example.cosmosclient.entities.jsons.GroupJson; import com.example.cosmosclient.resources.GroupsRest; import com.example.cosmosclient.resources.LocationRest; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.TimeZone; import java.util.Timer; import java.util.TimerTask; import java.util.UUID; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; import static android.app.Notification.EXTRA_NOTIFICATION_ID; public class CosmosBackgroundService extends IntentService implements LocationListener { private Context context; private LocationManager locationManager; private static final int MinTime = 1000;/*最小時間間隔*/ private static final float MinDistance = 1;/*最小距離間隔*/ private static boolean isServiceRunning = false; //サービスが起動しているかを確認 private String TAG = CosmosBackgroundService.class.getSimpleName(); private List<Notification> notifications =null; private CosmosLocation LOC = new CosmosLocation(); private double leftLat,leftLon,rightLat,rightLon; //ここに計算予定 private int count = 0; private int areacount = 0; private ArrayList<Feature> Electrical = new ArrayList<>(); private ArrayList<Feature> Home = new ArrayList<>(); private ArrayList<Feature> Phone = new ArrayList<>(); private ArrayList<Feature> PC = new ArrayList<>(); private ArrayList<Feature> Convenience = new ArrayList<>(); private ArrayList<Feature> Supermarket = new ArrayList<>(); private AreaInformation areaInfo = null; private NotificationManager notificationManager; public CosmosBackgroundService() { super("CosmosBackgroundService"); Log.d(TAG, "Constructor"); } @Override public void setIntentRedelivery(boolean enabled) { Log.d(TAG, "setIntentRedelivery"); super.setIntentRedelivery(enabled); } @Override public void onCreate() { Log.d(TAG, "onCreate"); super.onCreate(); context = getApplicationContext(); // LocationManager インスタンス生成 locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); int importance = NotificationManager.IMPORTANCE_HIGH; NotificationChannel mChannel = new NotificationChannel("cosmos", "cosmosChannel", importance); notificationManager.createNotificationChannel(mChannel); } @Override public void onStart(@Nullable Intent intent, int startId) { Log.d(TAG, "onStart"); super.onStart(intent, startId); } @RequiresApi(api = Build.VERSION_CODES.O) @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand"); if(!isServiceRunning) { int requestCode = 0; String channelId = "default"; String title = context.getString(R.string.app_name); PendingIntent pendingIntent = PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT); // ForegroundにするためNotificationが必要、Contextを設定 NotificationManager notificationManager = (NotificationManager) context. getSystemService(Context.NOTIFICATION_SERVICE); // Notification Channel 設定 NotificationChannel channel = new NotificationChannel( channelId, title, NotificationManager.IMPORTANCE_DEFAULT); channel.setDescription("Silent Notification"); // 通知音を消さないと毎回通知音が出てしまう // この辺りの設定はcleanにしてから変更 channel.setSound(null, null); // 通知ランプを消す channel.enableLights(false); channel.setLightColor(Color.BLUE); // 通知バイブレーション無し channel.enableVibration(false); if (notificationManager != null) { notificationManager.createNotificationChannel(channel); android.app.Notification notification = new android.app.Notification.Builder(context, channelId) .setContentTitle(title) // 本来なら衛星のアイコンですがandroid標準アイコンを設定 .setSmallIcon(android.R.drawable.btn_star) .setContentText("GPS") .setAutoCancel(true) .setContentIntent(pendingIntent) .setWhen(System.currentTimeMillis()) .build(); // startForeground startForeground(1, notification); } startGPS(); Timer timer = new Timer(); TimerTask task = new TimerTask() { @Override public void run() { startUpdateRequest(); } }; //timer.scheduleAtFixedRate(定期的に実行したいタスク,初回のタスク実行までの時間(ms),実行するタスクの間隔(ms)); timer.scheduleAtFixedRate(task, 10000, 30000); isServiceRunning = true; } return START_NOT_STICKY; } @RequiresApi(api = Build.VERSION_CODES.O) @Override protected void onHandleIntent(Intent intent) { Log.d(TAG, "onHandleIntent"); } protected void startGPS() { Log.d(TAG, "startGPS"); final boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); if (!gpsEnabled) { // GPSを設定するように促す enableLocationSettings(); } if (locationManager != null) { try { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return; } // Log.d(TAG, "requestLocationUpdates:"); locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MinTime, MinDistance, this); } catch (Exception e) { // Log.d(TAG, "Exception:"); e.printStackTrace(); } } else { } } @Override public void onDestroy() { Log.d(TAG, "onDestroy"); super.onDestroy(); } @Override public void onConfigurationChanged(Configuration newConfig) { Log.d(TAG, "onConfigurationChanged"); super.onConfigurationChanged(newConfig); } @Override public void onLowMemory() { Log.d(TAG, "onLowMemory"); super.onLowMemory(); } @Override public void onTrimMemory(int level) { Log.d(TAG, "onTrimMemory"); super.onTrimMemory(level); } @Nullable @Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind"); return super.onBind(intent); } @Override public boolean onUnbind(Intent intent) { Log.d(TAG, "onUnbind"); return super.onUnbind(intent); } @Override public void onRebind(Intent intent) { Log.d(TAG, "onRebind"); super.onRebind(intent); } @Override public void onTaskRemoved(Intent rootIntent) { Log.d(TAG, "onTaskRemoved"); super.onTaskRemoved(rootIntent); } @Override protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { Log.d(TAG, "dump"); super.dump(fd, writer, args); } @Override public void onLocationChanged(final Location location) { final Cosmos cosmos = (Cosmos) getApplication(); // Log.d(TAG, "onLocationChanged"); Log.d(TAG, "lat:" + location.getLatitude()); Log.d(TAG, "lon:" + location.getLongitude()); new Thread(new Runnable() { private List<Notification> notifications; public void run() { updateAreaInformation(location); this.notifications=searchNotifications(cosmos,location); sendNotifications(CosmosBackgroundService.this, notifications); } }).start(); } private void sendNotifications(CosmosBackgroundService cosmosBackgroundService, List<Notification> notifications) { for (Notification notify: notifications) { int notificationId = 1; //以下はボタンについてだよ Intent intent = new Intent(cosmosBackgroundService, NotificationDone.class); //snoozeIntent.setAction(ACTION_NOTIFICATION_OFF); intent.putExtra(EXTRA_NOTIFICATION_ID, 0); //以下2行はテスト用。本来はNotificationから取ってくる。 intent.putExtra("gId", notify.getGroup().getgId()); intent.putExtra("rId", notify.getRequest().getrId()); int randamCode = (int)(Math.random()*100000); PendingIntent pendingIntent = PendingIntent.getBroadcast(cosmosBackgroundService, randamCode, intent, PendingIntent.FLAG_UPDATE_CURRENT); android.app.Notification.Action action = new android.app.Notification.Action(R.drawable.notification_icon, "ボタンだよ", pendingIntent); android.app.Notification notification = new android.app.Notification.Builder(cosmosBackgroundService) .setContentTitle(notify.getRequest().getProduct()) .setContentText("通知ないよう") .setSmallIcon(R.drawable.icon_no) .setChannelId("cosmos") // .setGroup("") .addAction(action) .build(); notificationManager.notify(notificationId, notification); } } @Override public void onStatusChanged(String provider, int status, Bundle extras) { Log.d(TAG, "onStatusChanged"); } @Override public void onProviderEnabled(String provider) { Log.d(TAG, "onProviderEnabled"); } @Override public void onProviderDisabled(String provider) { Log.d(TAG, "onProviderDisabled"); } private void startUpdateRequest() { final Cosmos app = (Cosmos) getApplication(); if(app.getuId() != null) { final String uId, token; uId = app.getuId(); token = app.getToken(); //retrofitの処理 final Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://nitta-lab-www.is.konan-u.ac.jp/cosmos/rest/") .addConverterFactory(JacksonConverterFactory.create()) .build(); //interfaceから実装を取得 final GroupsRest getGroupsService = retrofit.create(GroupsRest.class); final GroupsRest getRequestsService = retrofit.create(GroupsRest.class); //API呼び出しのための値入力 final Call<GroupListResponse> call = getGroupsService.getGroups(uId, token); //サーバからデータ受け取り call.enqueue(new Callback<GroupListResponse>() { //成功時 @Override public void onResponse(Call<GroupListResponse> call, Response<GroupListResponse> response) { if (response.isSuccessful()) { final GroupListResponse groupListresult = response.body(); Log.d(TAG, "GroupListResponseグループ情報通信取得しました"); new Thread(new Runnable() { public void run() { for (GroupJson groupJson : groupListresult.getGroups()) { app.setGroup(new Group(groupJson)); if (groupJson.getRequestHash() != app.getGroup(groupJson.getgId()).getRequestHash()) { //RequestList取得するための必要な情報 final String gId = groupJson.getgId(); final boolean detail = true; int quantity = 20; final Call<RequestList> requestsListByGidCall = getRequestsService.getRequestsListByGid(gId, token, detail, quantity); Response<RequestList> requestListResponse; try { requestListResponse = requestsListByGidCall.execute(); if (requestListResponse.isSuccessful()) { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo")); RequestList requestList = requestListResponse.body(); app.getGroup(groupJson.getgId()).setRequestList(requestList); app.getGroup(groupJson.getgId()).setRequestHash(groupJson.getRequestHash()); Log.d(TAG, groupJson.getName() + "のRequestListを取得しました"); } else { // onFailure try { System.out.println(requestListResponse.errorBody().string()); } catch (IOException e) { e.printStackTrace(); } //onFailureでキャッチできないエラーの処理 Log.d(TAG, groupJson.getName() + "の通信エラー"); } } catch (IOException e) { e.printStackTrace(); Log.d(TAG, groupJson.getName() + "のRequestListの取得失敗しました"); } } } } }).start(); } else { //onFailureでキャッチできないエラー用 Log.d(TAG, "GroupListResponseグループ情報通信エラー"); } } //失敗時 @Override public void onFailure(Call<GroupListResponse> call, Throwable t) { t.printStackTrace(); Log.d(TAG, "GroupListResponseグループ情報取得失敗"); } }); } else { Log.d(TAG, "app.getuId()がnull"); } } private void enableLocationSettings() { Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(settingsIntent); } private void updateAreaInformation(final Location location){ Cosmos app = (Cosmos) getApplication(); count = 0; LOC.setLatitude(location.getLatitude()); LOC.setLongitude(location.getLongitude()); if(app.AreaInfoGetKey().size() != 0) { for (int i = 0; i < app.AreaInfoGetKey().size(); i++) { if (LOC.hashCode() != app.AreaInfoGetKey().get(i)) { count++; } } } if (count == 0) { //retrofitの処理 final Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://nitta-lab-www.is.konan-u.ac.jp/cosmos/rest/") .addConverterFactory(JacksonConverterFactory.create()) .build(); //interfaceから実装を取得 final LocationRest LocationService = retrofit.create(LocationRest.class); leftLat = Math.floor((location.getLatitude() + 0.1) * 10.0) / 10.0; leftLon = Math.floor(location.getLongitude() * 10.0) / 10.0; rightLat = Math.floor(location.getLatitude() * 10.0) / 10.0; rightLon = Math.floor((location.getLongitude() + 0.1) * 10.0) / 10.0; //API呼び出しのための値入力 Call<ArrayList<Feature>> call = LocationService.LocationService(String.valueOf(leftLat), String.valueOf(leftLon), String.valueOf(rightLat), String.valueOf(rightLon)); Response<ArrayList<Feature>> areaInformationResponse; try{ areaInformationResponse = call.execute(); if (areaInformationResponse.isSuccessful()) { areaInfo = new AreaInformation(); ArrayList<Feature> result = areaInformationResponse.body(); for(int i = 0; i < result.size(); i++){ switch (result.get(i).getCode()) { case 203001: Electrical.add(result.get(i)); areaInfo.setFeature(Electrical.get(0).getCode(),Electrical); break; case 203002: Home.add(result.get(i)); areaInfo.setFeature(Home.get(0).getCode(),Home); break; case 203003: Phone.add(result.get(i)); areaInfo.setFeature(Phone.get(0).getCode(),Phone); break; case 203004: PC.add(result.get(i)); areaInfo.setFeature(PC.get(0).getCode(),PC); break; case 205001: Convenience.add(result.get(i)); areaInfo.setFeature(Convenience.get(0).getCode(),Convenience); break; case 205002: Supermarket.add(result.get(i)); areaInfo.setFeature(Supermarket.get(0).getCode(),Supermarket); break; default: continue; } } //app/Cosmosに情報保存 app = (Cosmos) getApplication(); areaInfo.setLocation(LOC); app.setAreaInfo(areaInfo.getLocation().hashCode(),areaInfo); } else { //onFailureでキャッチできないエラー用 Toast.makeText(CosmosBackgroundService.this, "区画情報取得失敗", Toast.LENGTH_LONG).show(); } }catch (IOException e){ e.printStackTrace(); Log.d(TAG, "区画情報を取得できていません。"); } //サーバからデータ受け取り // call.enqueue(new Callback<ArrayList<Feature>>() { // //成功時 // @Override // public void onResponse(Call<ArrayList<Feature>> call, Response<ArrayList<Feature>> response) { // if (response.isSuccessful()) { // ArrayList<Feature> result = response.body(); // for(int i = 0; i < result.size(); i++){ // switch (result.get(i).getCode()) { // case 203001: // Electrical.add(result.get(i)); // areaInfo.setFeature(Electrical.get(0).getCode(),Electrical); // break; // case 203002: // Home.add(result.get(i)); // areaInfo.setFeature(Home.get(0).getCode(),Home); // break; // case 203003: // Phone.add(result.get(i)); // areaInfo.setFeature(Phone.get(0).getCode(),Phone); // break; // case 203004: // PC.add(result.get(i)); // areaInfo.setFeature(PC.get(0).getCode(),PC); // break; // case 205001: // Convenience.add(result.get(i)); // areaInfo.setFeature(Convenience.get(0).getCode(),Convenience); // break; // case 205002: // Supermarket.add(result.get(i)); // areaInfo.setFeature(Supermarket.get(0).getCode(),Supermarket); // break; // default: // continue; // } // } // // //app/Cosmosに情報保存 // Cosmos app = (Cosmos) getApplication(); // app.setAreaInfo(areaInfo.getLocation().hashCode(),areaInfo); // // } else { // //onFailureでキャッチできないエラー用 // Toast.makeText(CosmosBackgroundService.this, // "区画情報取得失敗", Toast.LENGTH_LONG).show(); // } // } // // //失敗時 // @Override // public void onFailure(Call<ArrayList<Feature>> call, Throwable t) { // t.printStackTrace(); // Toast.makeText(CosmosBackgroundService.this, // "区画情報の更新に失敗しました", Toast.LENGTH_SHORT).show(); // } // }); } } public List<Notification> searchNotifications(Cosmos cosmos, Location location){ double NowLat =location.getLatitude(); double NowLon =location.getLongitude(); //業番:notification HashMap<Integer, ArrayList<Notification>> codeToNotification = new HashMap<>(); //業番:feature HashMap<Integer, ArrayList<Feature>> codeToFeature = new HashMap<>(); ArrayList<Group> groups = new ArrayList<>(cosmos.getGroups()); //現在時刻取得 Date nowDate = new Date(); //codeToNotificationを作成。Featureはnull for(int i = 0 ; i < groups.size(); i++) { //requestのArrayListを回す for (int j = 0; j < groups.get(i).getRequestList().getRequests().size(); j++) { //codeToNotificationに格納するArrayList<Notification>を初期化 ArrayList<Notification> notifications = new ArrayList<>(); Date deadline = groups.get(i).getRequestList().getRequests().get(j).getDeadline(); //期限内かつ未達成のrequestのみ取得 if (( deadline == null || deadline.after(nowDate))&& groups.get(i).getRequestList().getRequests().get(j).isDone() == false) { //notificationを作成 Notification notification = new Notification(groups.get(i).getRequestList().getRequests().get(j),groups.get(i),null); //指定したkey(業番)を持っているHashMapがなければ作成する。 if(codeToNotification.get(groups.get(i).getRequestList().getRequests().get(j).getLocation())==null){ notifications.add(notification); codeToNotification.put(groups.get(i).getRequestList().getRequests().get(j).getLocation(),notifications); }else{ //keyをすでに持っている場合はArrayList<Notification>にnotification単体を追加していく codeToNotification.get(groups.get(i).getRequestList().getRequests().get(j).getLocation()).add(notification); } } } } //cosmosから区画情報から取得 CosmosLocation cosmosLocation = new CosmosLocation(); cosmosLocation.setLatitude(NowLat); cosmosLocation.setLongitude(NowLon); int areaInfoId = cosmosLocation.hashCode(); codeToFeature=cosmos.getAreaInfo(areaInfoId).getFeature(); //codeToFeatureの中の50m圏外Featureを削除していく。 //HashMapを順番に実行 for(HashMap.Entry<Integer, ArrayList<Feature>> e : codeToFeature.entrySet()) { //ArrayList<Feature>>を回していく for(int i=e.getValue().size() - 1; i >= 0;i--) { float[] distance = getDistance(NowLat, NowLon, e.getValue().get(i).getLocation().getLatitude(), e.getValue().get(i).getLocation().getLongitude()); // distance[0] = [2点間の距離] //50m圏外のFeatureを削除 if (distance[0] > 50) { e.getValue().remove(i); } } //codeToNotificationのkeyとcodeToFeatureのkeyが一致すれば //codeToFeatureのArray<Feature>をcodeToNotificationのArray<Feature>featuresに代入する if(codeToNotification.containsKey(e.getKey())==true){ for(int j=0;j<codeToNotification.get(e.getKey()).size();j++) { codeToNotification.get(e.getKey()).get(j).setFeatures(e.getValue()); } } } ArrayList<Notification> result = new ArrayList<Notification>(); for (ArrayList<Notification> list: codeToNotification.values()) { result.addAll(list); } return result; } public float[] getDistance(double x, double y, double x2, double y2) { // 結果を格納するための配列を生成 float[] results = new float[3]; // results[0] = [2点間の距離] // results[1] = [始点から見た方位角] // results[2] = [終点から見た方位角] // 距離計算 Location.distanceBetween(x, y, x2, y2, results); return results; } }