2017-05-08 8 views
1

이렇게 몇 가지 질문을 보았지만 아무도 내 문제를 해결하지 못했습니다. 나는 AlarmManager를 통해 백그라운드 서비스를 시작하고있다. 그것을Android - 취소 된 알림이 계속 나타납니다.

  1. 사용자가 GPS를 사용하고자하는 경우 :이 대안 경로를 따라 매번 서비스는 된 SharedPreferences에서 비활성화되었으며,하지 않을 경우, 그 자체의 새로운 인스턴스를 다시 예약하는 경우가 확인, 시작하고 계속 사용자의 위치를 ​​기다리고 을 사용하여 REST 끝점을 호출합니다.
  2. 사용자가 GPS를 사용하지 않으려는 경우 prefs에 저장된 위치를 사용합니다.

는 HTTP 호출 (제한 시간 : 30 초) 결과는 (이 발견 얼마나 많은 "가까운 객체"에 따라) 0- N 알림을 생성하는 된 JSONObject입니다.

내 문제는 알림입니다. 사용자가 취소하거나 열어도 알림이 표시되지 않는 것처럼 자주 다시 나타납니다. 웹 서비스가 매번 업데이트되는 제외 된 개체 ID 목록을 받기 때문에 결코 발생하지 않아야합니다. 여기

코드 :

ScannerService.java

package com.kiulomb.itascanner.service; 

import android.app.ActivityManager; 
import android.app.AlarmManager; 
import android.app.Notification; 
import android.app.NotificationManager; 
import android.app.PendingIntent; 
import android.app.Service; 
import android.content.ComponentName; 
import android.content.Context; 
import android.content.Intent; 
import android.content.SharedPreferences; 
import android.content.res.Resources; 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.location.Address; 
import android.location.Geocoder; 
import android.location.Location; 
import android.location.LocationManager; 
import android.media.RingtoneManager; 
import android.net.ConnectivityManager; 
import android.net.NetworkInfo; 
import android.net.Uri; 
import android.os.Bundle; 
import android.os.IBinder; 
import android.support.v4.app.NotificationCompat; 
import android.support.v4.content.ContextCompat; 
import android.util.Log; 

import com.android.volley.RequestQueue; 
import com.android.volley.toolbox.Volley; 
import com.kiulomb.itascanner.R; 
import com.kiulomb.itascanner.network.HTTPRequestManager; 
import com.kiulomb.itascanner.network.HTTPResponseListener; 
import com.kiulomb.itascanner.network.URLs; 
import com.kiulomb.itascanner.pref.FilterPreferencesManager; 
import com.kiulomb.itascanner.pref.NotificationsHistoryManager; 
import com.kiulomb.itascanner.pref.PrefConstants; 
import com.kiulomb.itascanner.utils.Haversine; 
import com.kiulomb.itascanner.utils.MyConfiguration; 

import org.json.JSONArray; 
import org.json.JSONObject; 

import java.io.IOException; 
import java.io.InputStream; 
import java.net.HttpURLConnection; 
import java.net.URL; 
import java.text.DateFormat; 
import java.text.SimpleDateFormat; 
import java.util.Calendar; 
import java.util.List; 
import java.util.Locale; 
import java.util.Timer; 
import java.util.TimerTask; 

public class ScannerService extends Service { 
    private static final String TAG = ScannerService.class.getSimpleName(); 

    private boolean locationFound = false; 
    private boolean withoutLocation = false; 
    private LocationManager mLocationManager = null; 

    private final Timer myTimer = new Timer(); 
    private final long TIMEOUT = 20000; 
    TimerTask myTask = new TimerTask() { 
     public void run() { 
      try { 
       Log.i(TAG, "Timeout is over, trying to stop service (location found? " + locationFound + ")"); 
       if (!locationFound) { 
        stopSelf(); 
       } 
      } catch (Exception e) { 
       Log.e(TAG, "Could not stop service after time: " + e.getMessage()); 
      } 
     } 
    }; 

    private LocationListener[] mLocationListeners = new LocationListener[] { 
      new LocationListener(LocationManager.GPS_PROVIDER), 
      new LocationListener(LocationManager.NETWORK_PROVIDER) 
    }; 

    private boolean alreadySearching = false; 

    private class LocationListener implements android.location.LocationListener { 
     Location mLastLocation; 

     LocationListener(String provider) { 
      Log.i(TAG, "LocationListener is " + provider); 
      mLastLocation = new Location(provider); 
     } 

     @Override 
     public void onLocationChanged(final Location location) { 
      Log.i(TAG, "onLocationChanged: " + location); 

      if (withoutLocation) { 
       return; 
      } 

      ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 

      NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); 
      boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting(); 

      if (location != null) { 
       if (isConnected) { 
        mLastLocation.set(location); 
        locationFound = true; 

        Log.i(TAG, "already searching? " + alreadySearching); 
        if (!alreadySearching) { 
         findClosest(location.getLatitude(), location.getLongitude()); 
        } 
        alreadySearching = true; 
       } else { 
        Log.e(TAG, "no connectivity, ending service"); 
        stopSelf(); 
       } 
      } else { 
       Log.e(TAG, "no position, ending service"); 
       stopSelf(); 
      } 
     } 

     @Override 
     public void onProviderDisabled(String provider) { 
      Log.i(TAG, "onProviderDisabled: " + provider); 
     } 

     @Override 
     public void onProviderEnabled(String provider) { 
      Log.i(TAG, "onProviderEnabled: " + provider); 
     } 

     @Override 
     public void onStatusChanged(String provider, int status, Bundle extras) { 
      Log.i(TAG, "onStatusChanged: " + provider); 
     } 
    } 

    private void initializeLocationManager() { 
     Log.d(TAG, "initializeLocationManager"); 
     if (mLocationManager == null) { 
      mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE); 
     } 
    } 

    @Override 
    public IBinder onBind(Intent arg0) { 
     return null; 
    } 

    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 
     Log.d(TAG, "onStartCommand"); 
     // super.onStartCommand(intent, flags, startId); 
     return START_STICKY; 
    } 

    @Override 
    public void onCreate() { 
     Log.d(TAG, "onCreate"); 

     SharedPreferences pref = getSharedPreferences(PrefConstants.PREF_APP_FILE, MODE_PRIVATE); 
     if (pref.getBoolean(PrefConstants.PREF_APP_SERVICE_ENABLED, PrefConstants.PREF_APP_SERVICE_ENABLED_DEFAULT)) { 
      Intent intent = new Intent(ScannerService.this, ScannerService.class); 
      PendingIntent pintent = PendingIntent.getService(ScannerService.this, 0, intent, 0); 
      AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 
      Calendar cal = Calendar.getInstance(); 
      alarm.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis() + 60000, pintent); // or setExact() // TODO custom time 
      // alarm.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 60000, pintent); 

      if (!pref.getBoolean(PrefConstants.PREF_APP_SERVICE_CUSTOMCENTER, PrefConstants.PREF_APP_SERVICE_CUSTOMCENTER_DEFAULT)) { 
       // use GPS 
       initializeLocationManager(); 
       try { 
        mLocationManager.requestLocationUpdates(
          LocationManager.NETWORK_PROVIDER, 
          MyConfiguration.LOCATION_INTERVAL, 
          MyConfiguration.LOCATION_DISTANCE, 
          mLocationListeners[1]); 
       } catch (SecurityException ex) { 
        Log.e(TAG, "fail to request location update, ignore", ex); 
       } catch (IllegalArgumentException ex) { 
        Log.e(TAG, "network provider does not exist, " + ex.getMessage()); 
       } 

       try { 
        mLocationManager.requestLocationUpdates(
          LocationManager.GPS_PROVIDER, 
          MyConfiguration.LOCATION_INTERVAL, 
          MyConfiguration.LOCATION_DISTANCE, 
          mLocationListeners[0]); 
       } catch (SecurityException ex) { 
        Log.e(TAG, "fail to request location update, ignore", ex); 
       } catch (IllegalArgumentException ex) { 
        Log.e(TAG, "gps provider does not exist " + ex.getMessage()); 
       } 
      } else { 
       withoutLocation = true; 

       // do not use GPS 
       String[] savedNotifCenter = pref.getString(PrefConstants.PREF_APP_SERVICE_CENTER, PrefConstants.PREF_APP_SERVICE_CENTER_DEFAULT).split(","); 
       double savedLat = Double.parseDouble(savedNotifCenter[0]); 
       double savedLng = Double.parseDouble(savedNotifCenter[1]); 

       locationFound = true; // prevent the service from stopping 
       findClosest(savedLat, savedLng); 
      } 
     } else { 
      stopSelf(); 
      return; 
     } 

     /*if (isForeground(getPackageName())) { 
      Log.i(getClass().getSimpleName(), "application is in foreground, stopping service"); 
      stopSelf(); 
      return; 
     }*/ 

     myTimer.schedule(myTask, TIMEOUT); 
    } 

    public boolean isForeground(String myPackage) { 
     ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); 
     List<ActivityManager.RunningTaskInfo> runningTaskInfo = manager.getRunningTasks(1); 
     ComponentName componentInfo = runningTaskInfo.get(0).topActivity; 
     return componentInfo.getPackageName().equals(myPackage); 
    } 

    @Override 
    public void onDestroy() { 
     Log.d(TAG, "onDestroy"); 
     super.onDestroy(); 
     if (mLocationManager != null) { 
      for (LocationListener mLocationListener : mLocationListeners) { 
       try { 
        mLocationManager.removeUpdates(mLocationListener); 
       } catch (SecurityException se) { 
        Log.e(TAG, "security exception", se); 
       } catch (Exception ex) { 
        Log.e(TAG, "fail to remove location listeners, ignore", ex); 
       } 
      } 
     } 
    } 

    private void findClosest(final double lat, final double lng) { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       String url = URLs.buildURL(URLs.NOTIFICATIONS); 
       url += "?lat=" + lat; 
       url += "&lng=" + lng; 

       final SharedPreferences pref = getSharedPreferences(PrefConstants.PREF_APP_FILE, MODE_PRIVATE); 
       if (pref.contains(PrefConstants.PREF_APP_SERVICE_RADIUS)) { 
        url += "&radius=" + pref.getInt(PrefConstants.PREF_APP_SERVICE_RADIUS, PrefConstants.PREF_APP_SERVICE_RADIUS_DEFAULT); 
       } 

       url += "&limit=" + PrefConstants.PREF_APP_MAP_LIMIT_DEFAULT; 

       if (pref.contains(PrefConstants.PREF_APP_SERVICE_IV)) { 
        url += "&iv=" + pref.getInt(PrefConstants.PREF_APP_SERVICE_IV, PrefConstants.PREF_APP_SERVICE_IV_DEFAULT); 
       } 

       String exclusionsNumbers = getExcludedNumbersParam(); 
       if (exclusionsNumbers.length() > 0) { 
        url += "&exNum=" + exclusionsNumbers; 
       } 

       final NotificationsHistoryManager notificationsHistoryManager = new NotificationsHistoryManager(ScannerService.this); 
       final List<Long> excludedIds = notificationsHistoryManager.getAlreadyFoundObjects(); 
       String exclusionsIds = getExcludedIdsParam(excludedIds); 
       if (exclusionsIds.length() > 0) { 
        url += "&exId=" + exclusionsIds; 
       } 

       /*final long lastId = pref.getLong(PrefConstants.PREF_SERVICE_LAST_ID, 0L); 
       url += "&li=" + lastId;*/ 

       final Context context = ScannerService.this; 
       HTTPRequestManager requestManager = new HTTPRequestManager(context, url, true, null, new HTTPResponseListener() { 
        @Override 
        public void onSuccess(JSONObject response) { 
         try { 
          JSONArray responseArray = response.getJSONArray("objects"); 
          final String foundString = getString(R.string.found); 
          final String inCityString = getString(R.string.in_city); 
          final String expiringString = getString(R.string.expiring); 
          final DateFormat sdf = SimpleDateFormat.getTimeInstance(); 
          final Resources res = getResources(); 
          final String packageName = getPackageName(); 
          final String mapsApiKey = getString(R.string.google_maps_key); 
          final boolean notifClickAutoCancel = pref.getBoolean(PrefConstants.PREF_APP_SERVICE_NOTIFCANCEL, PrefConstants.PREF_APP_SERVICE_NOTIFCANCEL_DEFAULT); 
          final boolean notifExpiredAutoCancel = pref.getBoolean(PrefConstants.PREF_APP_SERVICE_NOTIFCANCELEXPIRED, PrefConstants.PREF_APP_SERVICE_NOTIFCANCELEXPIRED_DEFAULT); 
          final boolean mapPicture = pref.getBoolean(PrefConstants.PREF_APP_SERVICE_MAPPICTURE, PrefConstants.PREF_APP_SERVICE_MAPPICTURE_DEFAULT); 
          final Locale defaultLocale = Locale.getDefault(); 

          Calendar calendar = Calendar.getInstance(); 
          // long maxId = lastId; 
          for (int i = 0; i < responseArray.length(); i++) { 
           try { 
            final MyEntity p = MyEntity.fromJSONLight(responseArray.getJSONObject(i)); 
            // it should never happen, but notifications are shown many times :/ 
            if (!excludedIds.contains(p.getId())) { 
             excludedIds.add(p.getId()); 
             // maxId = Math.max(p.getId(), maxId); 
             final double iv = p.getIV(); 
             final long expirationFixed = (p.getDisappearTime() - System.currentTimeMillis() - 2000); 

             final Calendar expirationTime = (Calendar) calendar.clone(); 
             // now.add(Calendar.SECOND, (int) ((p.getDisappearTime() - System.currentTimeMillis()/1000) - 2)); 
             expirationTime.setTimeInMillis(expirationTime.getTimeInMillis() + expirationFixed); 

             final int distance = (int) Math.round(1000 * Haversine.distance(lat, lng, p.getLatitude(), p.getLongitude())); 

             String cityName = null; 
             Geocoder gcd = new Geocoder(context, defaultLocale); 
             List<Address> addresses = gcd.getFromLocation(p.getLatitude(), p.getLongitude(), 1); 
             if (addresses.size() > 0) { 
              cityName = addresses.get(0).getLocality(); 
             } 
             final String cityNameParam = cityName; 
             new Thread(new Runnable() { 
              @Override 
              public void run() { 
               sendNotification((int) (p.getId()), 
                   foundString + " " + p.getName() + (iv > 0 ? " " + iv + "%" : "") + (cityNameParam != null ? " " + inCityString + " " + cityNameParam : ""), 
                   expiringString + " " + sdf.format(expirationTime.getTime()) + " - " + distance + "m" + (movesStringParam != null ? " (" + movesStringParam + ")" : ""), 
                   p, 
                   res, 
                   packageName, 
                   notifClickAutoCancel, 
                   notifExpiredAutoCancel, 
                   expirationFixed, 
                   mapsApiKey, 
                   mapPicture); 
              } 
             }).start(); 
            } 
           } catch (Exception e) { 
            Log.e(TAG, "error", e); 
           } 
          } 

          notificationsHistoryManager.saveAlreadyFoundObjects(excludedIds); 

          stopSelf(); 
         } catch (Exception e) { 
          Log.e(TAG, "error in reading JSONArray", e); 
          stopSelf(); 
         } 
        } 

        @Override 
        public void onError(int errorCode) { 
         stopSelf(); 
        } 
       }); 

       RequestQueue requestQueue = Volley.newRequestQueue(context); 
       requestQueue.add(requestManager); 
      } 
     }).start(); 
    } 

    private String getExcludedNumbersParam() { 
     String exclusionsNumbers = ""; 
     List<Integer> excludedNumbers = new FilterPreferencesManager(ScannerService.this).getNotificationsExcludedNumbers(); 
     int sizeNumbers = excludedNumbers.size(); 
     for (int i = 0; i < sizeNumbers; i++) { 
      exclusionsNumbers += excludedNumbers.get(i); 

      if (i < sizeNumbers - 1) { 
       exclusionsNumbers += ","; 
      } 
     } 

     return exclusionsNumbers; 
    } 

    private String getExcludedIdsParam(List<Long> excludedIds) { 
     String exclusionsIds = ""; 

     int sizeIds = excludedIds.size(); 
     for (int i = 0; i < sizeIds; i++) { 
      exclusionsIds += excludedIds.get(i); 

      if (i < sizeIds - 1) { 
       exclusionsIds += ","; 
      } 
     } 
     return exclusionsIds; 
    } 

    private Locale locale = Locale.getDefault(); 

    private void sendNotification(final int notificationId, 
      final String title, 
      final String message, 
      final MyEntity entity, 
      final Resources res, 
      final String packageName, 
      final boolean autoClickCancel, 
      final boolean autoExpiredCancel, 
      final long expirationFromNow, 
      final String mapsApiKey, 
      final boolean mapPicture) { 

     final double entityLat = entity.getLatitude(); 
     final double entityLng = entity.getLongitude(); 

     Intent mapIntent = null; 
     try { 
      String urlAddress = "http://maps.google.com/maps?q=" + entityLat + "," + entityLng; 
      mapIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(urlAddress)); 
     } catch (Exception e) { 
      Log.e(TAG, "error in notification intent preparation", e); 
     } 
     PendingIntent pendingIntent = PendingIntent.getActivity(ScannerService.this, 0, mapIntent, PendingIntent.FLAG_CANCEL_CURRENT); 

     Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); 

     int drawable = res.getIdentifier("entity" + String.format(locale, "%04d", entity.getNumber()) + "big", "drawable", packageName); 
     final NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(ScannerService.this) 
       .setSmallIcon(drawable) 
       .setContentTitle(title) 
       .setContentText(message) 
       .setAutoCancel(autoClickCancel) 
       .setSound(defaultSoundUri) 
       .setPriority(Notification.PRIORITY_HIGH) 
       .setLights(ContextCompat.getColor(ScannerService.this, R.color.colorPrimary), 500, 2000); 

     if (mapPicture) { 
      String imageUrl = "https://maps.googleapis.com/maps/api/staticmap" 
        + "?center=" + entityLat + "," + entityLng 
        + "&zoom=14" 
        + "&scale=false" 
        + "&size=450x275" 
        + "&maptype=roadmap" 
        + "&key=" + mapsApiKey 
        + "&format=jpg" 
        + "&visual_refresh=true"; 

      Log.i(getClass().getSimpleName(), "generated url for notification image: " + imageUrl); 
      Bitmap bmURL = getBitmapFromURL(imageUrl); 
      if (bmURL != null) { 
       notificationBuilder.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(bmURL)); 
      } 
     } 

     if (mapIntent != null) { 
      notificationBuilder.setContentIntent(pendingIntent); 
     } 

     if (autoExpiredCancel) { 
      Log.i(getClass().getSimpleName(), "setting notification timer for expiration, id: " + notificationId + ", expiring in " + expirationFromNow + "ms"); 

      Timer timer = new Timer(); 
      timer.schedule(new TimerTask() { 
       @Override 
       public void run() { 
        Log.i(getClass().getSimpleName(), "canceling notification expired, id: " + notificationId); 
        notificationManager.cancel(notificationId); 
       } 
      }, expirationFromNow); 
     } 
    } 
    // } 

    private Bitmap getBitmapFromURL(String strURL) { 
     try { 
      URL url = new URL(strURL); 
      HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 
      connection.setDoInput(true); 
      connection.connect(); 
      InputStream input = connection.getInputStream(); 
      return BitmapFactory.decodeStream(input); 
     } catch (IOException e) { 
      e.printStackTrace(); 
      return null; 
     } 
    } 
} 

알림 이력 관리

import android.content.Context; 
import android.content.SharedPreferences; 
import android.util.Log; 

import java.util.ArrayList; 
import java.util.HashSet; 
import java.util.List; 
import java.util.Set; 

public class NotificationsHistoryManager { 

    private final static String PREF_FILE = "nh"; 
    private final static String PREF_FOUND_KEY = "f"; 

    private SharedPreferences pref; 

    public NotificationsHistoryManager(Context context) { 
     pref = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 
    } 

    public void saveAlreadyFoundObjects(List<Long> found) { 
     Set<String> idsString = new HashSet<>(); 
     int size = found.size(); 
     for (int i = Math.max(0, size - 200); i < size; i++) { 
      long f = found.get(i); 
      idsString.add(f + ""); 
     } 

     pref.edit().putStringSet(PREF_FOUND_KEY, idsString).apply(); 
    } 

    public List<Long> getAlreadyFoundObjects() { 
     List<Long> excluded = new ArrayList<>(); 

     for (String id : pref.getStringSet(PREF_FOUND_KEY, new HashSet<String>())) { 
      try { 
       excluded.add(Long.parseLong(id)); 
      } catch (Exception e) { 
       Log.e(getClass().getSimpleName(), "error in parsing string '" + id + "' to long id: " + e.getMessage()); 
      } 
     } 

     return excluded; 
    } 

    public void clean() { 
     pref.edit().clear().apply(); 
    } 
} 

참고 : MainActivity가 시작될 때 서비스의 인스턴스가 실행중인 경우, 그것은 검사하고 , 그렇지 않다면, AlarmManager로 새로운 것을 스케줄링합니다. 나는 이것이 그 문제의 원인이라고 생각했지만 서비스는 이미 알 수 있듯이 이미 통지 된 내용을 검사하고 건너 뜁니다. 환경 설정을 사용하여 START_STICKY를 NOT_STICKY (으)로 변경하여 중복 ID를 처리하고 동기화 작업을 시도했습니다 ... 그 외 무엇을 시도해야할지 모르겠습니다. 제발 도와주세요 :) 더 자세한 내용이 필요하면 그냥 물어보십시오.

감사합니다.

답변

0

내가 발견 한 것을 공유하려면 ... 문제가 무엇인지 이해했습니다. NotificationsHistoryManager 살펴보기 : 발견 된 개체 목록을 저장하기 위해 Set (SharedPreferences에서만 사용 가능한 유일한 "목록"개체 유형)을 사용하여 발견 된 마지막 200 개체 만 저장합니다 (이전 개체는 만료되어 의미가 없습니다. 그들을 지키십시오). 문제 : 세트는 목록이 아닙니다. 저장 한 200 개의 객체는 마지막에 추가 된 객체가 아니기 때문에, pref (getAlreadyFoundObjects())에서 읽을 때 세트로 작성되므로 "임의로"정렬됩니다. 사용자 지정 문자열 (쉼표로 구분 된 값)을 만들어 저장 한 방식을 변경해야만 원하는 순서대로 저장됩니다.

누군가 도움이되기를 바랍니다.