Categories
discuss

Functional way to create an array of numbers

How could I write the following code more functionally using ES6, without any 3rd party libraries?

// sample pager array
// * output up to 11 pages
// * the current page in the middle, if page > 5
// * don't include pager < 1 or pager > lastPage
// * Expected output using example:
//     [9,10,11,12,13,14,15,16,17,18,19]

const page = 14 // by example
const lastPage = 40 // by example
const pagerPages = page => {
  let newArray = []
  for (let i = page - 5; i <= page + 5; i++) {
    i >= 1 && i <= lastPage ? newArray.push(i) : null
  }
  return newArray
}

I would like to avoid Array.push, and possibly the for loop, but I’m not sure how I would achieve it in this situation.

Answer

Functional programming isn’t limited to reduce, filter, and map; it’s about functions. This means we don’t have to rely on perverse knowledge like Array.from ({ length: x }) where an object with a length property can be treated like an array. This kind of behavior is bewildering for beginners and mental overhead for anyone else. It think you’ll enjoy writing programs that encode your intentions more clearly.

reduce starts with 1 or more values and reduces to (usually) a single value. In this case, you actually want the reverse of a reduce (or fold), here called unfold. The difference is we start with a single value, and expand or unfold it into (usually) multiple values.

We start with a simplified example, alphabet. We begin unfolding with an initial value of 97, the char code for the letter a. We stop unfolding when the char code exceeds 122, the char code for the letter z.

const unfold = (f, initState) =>
  f ( (value, nextState) => [ value, ...unfold (f, nextState) ]
    , () => []
    , initState
    )

const alphabet = () =>
  unfold
    ( (next, done, char) =>
        char > 122
          ? done ()
          : next ( String.fromCharCode (char) // value to add to output
                 , char + 1                   // next state
                 )
    , 97 // initial state
    )
    
console.log (alphabet ())
// [ a, b, c, ..., x, y, z ]

Above, we use a single integer for our state, but other unfolds may require a more complex representation. Below, we show the classic Fibonacci sequence by unfolding a compound initial state of [ n, a, b ] where n is a decrementing counter, and a and b are numbers used to compute the sequence’s terms. This demonstrates unfold can be used with any seed state, even arrays or objects.

const fib = (n = 0) =>
  unfold
    ( (next, done, [ n, a, b ]) =>
        n < 0
          ? done ()
          : next ( a                   // value to add to output
                 , [ n - 1, b, a + b ] // next state
                 )
    , [ n, 0, 1 ] // initial state
    )

console.log (fib (20))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765 ]

Now we have the confidence to write pagination. Again, our initial state is compound data [ page, count ] as we need to keep track of the page to add, and how many pages (count) we’ve already added.

Another advantage to this approach is that you can easily parameterize things like 10 or -5 or +1 and there’s a sensible, semantic structure to place them in.

const unfold = (f, initState) =>
  f ( (value, nextState) => [ value, ...unfold (f, nextState) ]
    , () => []
    , initState
    )
    
const pagination = (totalPages, currentPage = 1) =>
  unfold
    ( (next, done, [ page, count ]) =>
        page > totalPages
          ? done ()
          : count > 10
            ? done ()
            : next (page, [ page + 1, count + 1 ])
    , [ Math.max (1, currentPage - 5), 0 ]
    )

console.log (pagination (40, 1))
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]

console.log (pagination (40, 14))
// [ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ]

console.log (pagination (40, 38))
// [ 33, 34, 35, 36, 37, 38, 39, 40 ]

console.log (pagination (40, 40))
// [ 35, 36, 37, 38, 39, 40 ]

Above, there are two conditions which result in a call to done (). We can collapse these using || and the code reads a little nicer

const pagination = (totalPages, currentPage = 1) =>
  unfold
    ( (next, done, [ page, count ]) =>
        page > totalPages || count > 10
          ? done ()
          : next (page, [ page + 1, count + 1 ])
    , [ Math.max (1, currentPage - 5), 0 ]
    )
Categories
discuss

Cannot receive messages through firebase after server reset

I have a mobile app that gets notifications from a server through firebase cloud messaging.

When I’m launching the mobile app and doing the subscription, the mobile app received the notification from the server.

The problem is when the server is going down and doing a restart. After this reset, the server is sending the mobile app the notification (with the same procedure as before), but the mobile app doesn’t receive anything. Remember that the mobile app stays the same (registered) meanwhile (the server doing the reset very quickly).

The IP and ports of the server stays the same…

Here is the mobile code to subscribe the right channel:

public class MainActivity extends AppCompatActivity  {

    private static final String TAG = "MainActivity";

    private static final String NEW_CONTACTS_TOPIC = "new_contacts";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // Create channel to show notifications.
            String channelId  = getString(R.string.default_notification_channel_id);
            String channelName = getString(R.string.default_notification_channel_name);
            NotificationManager notificationManager =
                    getSystemService(NotificationManager.class);
            notificationManager.createNotificationChannel(new NotificationChannel(channelId,
                    channelName, NotificationManager.IMPORTANCE_LOW));
        }

        if (getIntent().getExtras() != null) {
            for (String key : getIntent().getExtras().keySet()) {
                Object value = getIntent().getExtras().get(key);
                Log.d(TAG, "Key: " + key + " Value: " + value);
            }
        }

        Button subscribeButton = findViewById(R.id.subscribeButton);
        subscribeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FirebaseMessaging.getInstance().subscribeToTopic(NEW_CONTACTS_TOPIC);

                // Log and toast
                String msg = getString(R.string.msg_subscribed);
                Log.d(TAG, msg);
                Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
            }
        });

        Button logTokenButton = findViewById(R.id.logTokenButton);
        logTokenButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Get token
                String token = FirebaseInstanceId.getInstance().getToken();

                // Log and toast
                String msg = getString(R.string.msg_token_fmt, token);
                Log.d(TAG, msg);
                Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

public class MyFirebaseMessagingService extends FirebaseMessagingService {

    private static final String TAG = "MyFirebaseMsgService";

    /**
     * Called when message is received.
     *
     * @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
     */
    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {

        Log.d(TAG, "From: " + remoteMessage.getFrom());

        // Check if message contains a data payload.
        if (remoteMessage.getData().size() > 0) {
            Log.d(TAG, "Message data payload: " + remoteMessage.getData());
            try {
                handleNow(remoteMessage);
            } catch (IOException e) {
                Log.e(TAG, e.getMessage());
            } catch (InterruptedException e) {
                Log.e(TAG, e.getMessage());
            }
        }

        // Check if message contains a notification payload.
        if (remoteMessage.getNotification() != null) {
            Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
        }
    }
}

And here is the procedure within my server to send the notification:

@Service
public class AndroidPushNotificationsService {

private static final String FIREBASE_SERVER_KEY = "XXXX";
private static final String FIREBASE_API_URL = "https://fcm.googleapis.com/fcm/send";

@Async
public CompletableFuture<String> send(HttpEntity<String> entity) {

    RestTemplate restTemplate = new RestTemplate();

    ArrayList<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
    interceptors.add(new HeaderRequestInterceptor("Authorization", "key=" + FIREBASE_SERVER_KEY));
    interceptors.add(new HeaderRequestInterceptor("Content-Type", "application/json"));
    restTemplate.setInterceptors(interceptors);

    String firebaseResponse = restTemplate.postForObject(FIREBASE_API_URL, entity, String.class);

    return CompletableFuture.completedFuture(firebaseResponse);
}

IMPORTANT: while the server is up, the communication between the server and the mobile app can work for a long time…

How can I resolve this issue?

Answer

In some situations, FCM may not deliver a message. This occurs when there are too many messages (>100)

In Firebase docs says: onDeletedMessages() called when the FCM server deletes pending messages.

This may be due to:

  1. Too many messages stored on the FCM server. This can occur when an app’s servers send a bunch of non-collapsible messages to FCM servers while the device is offline.
  2. The device hasn’t connected in a long time and the app server has recently (within the last 4 weeks) sent a message to the app on that device.
Categories
discuss

Find if screen has rounded corners

I am looking for a way to determine whether Android device has screen with rounded corners and ideally also the radius.

My usecase is a game that consist of a single fullscreen OpenGL view. It renders some UI elements close to the edges and borders to give as much area as possible to the game itself. But when the screen has rounded corners, they would be partially hidden, so they need to be positioned differently.

Answer

Update as of 6th June 2019

Android officially supports display cutouts on devices running Android 9 (API level 28) and higher.

Reference: https://developer.android.com/guide/topics/display-cutout/

Currently (7th May 2018) you can only check if the physical screen is rounded or not (for Android Wear) using [isRounded()][1] method but can not check if screen corners are rounded.

I think there are only a few devices with rounded corners out there. So it’s better to just check the name of device and adjust your layout accordingly.

There is one popular Android library to get the market name of an Android device. Check here for more information: https://github.com/jaredrummler/AndroidDeviceNames/

Usage:

String deviceName = DeviceName.getDeviceName();

Hope this workaround will help you temporarily.

Maybe all devices in the future will release with round corner screens (just an assumption).

Categories
discuss

Java 8 lambda for each call method and addAll

I would like to replace the following code to utilize Java 8 streams if possible:

final List<Long> myIds = new ArrayList<>();
List<Obj> myObjects = new ArrayList<>();
// myObject populated...

for (final Obj ob : myObjects) {
   myIds.addAll(daoClass.findItemsById(ob.getId()));
}

daoClass.findItemsById returns List<Long>

Can anybody advise the best way to do this via lambdas? Many thanks.

Answer

List<Long> myIds = myObjects.stream()
    .map(Obj::getId)
    .map(daoClass::findItemsById)
    .flatMap(Collection::stream)
    .collect(Collectors.toList());
Categories
discuss

Room user configurable order by queries

I am migrating an app to use Room from normal Sqlite and one part that I am having trouble with is that there are several queries that have an order by statement that are user configurable, meaning they can change how they want to view the list order.

What it seems is the Room does not allow for dynamic order by statements so I would have to make individual queries specific for each order by statement.

Has anyone found a better way around this issue so I can have 1 query statement where the only thing that changes is the order by clause vs having to write what in my case would be about 15 extra query statements all basically the same?

Answer

In Room 1.1 there is now a RawQuery that can be used that solves this issue

 @Dao
 interface RawDao {
     @RawQuery
     User getUserViaQuery(SupportSQLiteQuery query);
 }
 SimpleSQLiteQuery query = new SimpleSQLiteQuery("SELECT * FROM User WHERE id = ? LIMIT 1",
         new Object[]{userId});
 User user2 = rawDao.getUserViaQuery(query);

https://developer.android.com/reference/android/arch/persistence/room/RawQuery

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..