Newer
Older
CosmosClient / app / src / main / java / com / example / cosmosclient / services / CosmosBackgroundService.java
Tomomi Sugisawa on 15 Nov 2019 22 KB Merge pull request #85 from nitta-lab-2019/AchieveRequests
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 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 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;
        //以下はボタンについてだよ
        Intent intent = new Intent(cosmosBackgroundService, NotificationDone.class);
        //snoozeIntent.setAction(ACTION_NOTIFICATION_OFF);
        intent.putExtra(EXTRA_NOTIFICATION_ID, 0);
        //以下2行はテスト用。本来はNotificationから取ってくる。
        intent.putExtra("gId", UUID.randomUUID().toString());
        intent.putExtra("rId", "rid2");
        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("通知タイトル")
                .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;

    }

}