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.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.Date;
import java.util.HashMap;
import java.util.List;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.jackson.JacksonConverterFactory;
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 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(Location location) {
final Cosmos cosmos = (Cosmos) getApplication();
// Log.d(TAG, "onLocationChanged");
Log.d(TAG, "lat:" + location.getLatitude());
Log.d(TAG, "lon:" + location.getLongitude());
updateAreaInformation(location);
this.notifications=searchNotifications(cosmos,location);
sendNotifications(this, notifications);
}
private void sendNotifications(CosmosBackgroundService cosmosBackgroundService, List<Notification> notifications) {
int notificationId = 1;
// MainActivity→broadcastreseiver(?)に変更
// Intent intent = new Intent(cosmosBackgroundService, MainActivity.class);
// 以下二行はボタンについてだよ
// PendingIntent pendingIntent = PendingIntent.getActivity(cosmosBackgroundService, 0, 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("通知タイトル")
.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(Location location) {
Cosmos app = (Cosmos) getApplication();
count = 0;
LOC.setLatitude(location.getLatitude());
LOC.setLongitude(location.getLongitude());
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));
//サーバからデータ受け取り
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();
//app/Cosmosに情報保存
Cosmos app = (Cosmos) getApplication();
// app.setAreaInfo();
} 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 =(ArrayList<Group>) 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 = null;
//期限内かつ未達成のrequestのみ取得
if (groups.get(i).getRequestList().getRequests().get(j).getDeadline().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(); 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;
}
}