/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2020 - Raw Material Software Limited

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).

   End User License Agreement: www.juce.com/juce-6-licence
   Privacy Policy: www.juce.com/juce-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{
// This byte-code is generated from native/java/com/rmsl/juce/ComponentPeerView.java with min sdk version 16
// See juce_core/native/java/README.txt on how to generate this byte-code.
static const uint8 javaComponentPeerView[] =
{31,139,8,8,174,115,161,94,0,3,106,97,118,97,67,111,109,112,111,110,101,110,116,80,101,101,114,86,105,101,119,46,100,101,120,0,
165,154,9,120,91,213,149,199,207,189,79,139,45,203,178,44,111,137,45,39,242,26,37,113,108,153,0,113,176,3,89,77,157,56,100,177,
146,18,59,52,60,75,207,182,18,249,61,69,146,237,4,232,16,150,38,41,100,90,104,3,221,82,186,165,25,74,51,109,153,161,148,73,75,
39,3,101,107,25,26,104,233,164,45,75,232,199,180,64,7,62,160,105,203,116,33,243,191,247,93,217,202,86,218,142,252,253,116,206,
59,247,220,253,222,115,175,100,197,141,29,158,200,252,139,40,84,84,16,62,178,99,227,93,78,119,55,171,63,246,210,255,110,45,251,
64,100,247,111,158,226,203,231,16,165,136,104,199,198,11,3,164,94,187,102,19,253,154,108,123,59,8,58,136,22,64,158,128,44,135,
204,186,137,12,200,242,2,34,13,178,171,136,232,238,25,68,7,32,127,82,69,116,28,156,0,239,128,63,130,83,128,79,35,114,129,66,80,
12,154,193,108,48,15,180,131,139,193,21,224,102,240,57,112,4,60,11,126,11,42,166,19,45,6,253,32,3,246,130,187,192,183,192,143,
192,187,160,172,26,101,1,3,236,6,247,131,231,65,97,13,81,7,216,5,190,13,94,0,60,72,84,15,86,128,45,32,13,238,4,223,1,79,131,147,
160,160,150,104,22,232,4,91,193,110,176,15,124,26,124,22,124,1,220,13,14,131,251,192,17,112,20,60,2,158,0,79,129,31,129,227,
224,5,240,11,240,26,120,29,188,13,222,1,127,6,12,227,230,2,30,224,3,245,96,1,88,6,214,128,1,48,12,50,96,23,216,11,238,0,159,3,
71,192,131,224,24,56,14,78,128,87,192,91,224,29,224,156,73,20,0,245,160,29,92,2,150,128,203,193,74,176,14,108,1,91,193,13,224,
54,240,105,112,8,220,7,254,29,252,24,188,8,222,4,239,2,119,136,200,15,106,65,27,184,12,68,65,12,100,192,237,224,19,224,179,224,
243,224,94,240,48,120,22,188,12,222,2,238,58,162,105,96,54,232,0,43,192,26,48,2,210,96,7,184,22,236,1,251,192,103,193,97,240,
32,120,28,252,4,188,0,94,3,39,193,41,80,80,79,84,7,34,96,1,232,6,189,32,10,54,131,45,192,0,38,200,130,107,192,63,130,47,130,251,
192,99,224,101,240,123,224,105,32,106,0,115,193,37,96,37,216,0,226,96,20,76,128,221,96,63,56,0,190,12,190,14,238,7,15,129,
39,192,147,224,77,224,107,196,250,5,213,96,14,232,0,235,128,14,44,240,97,240,121,240,32,248,62,248,41,248,111,240,91,160,53,97,
254,64,61,104,7,171,192,58,176,9,232,96,24,36,65,26,92,7,174,7,187,193,71,193,29,224,16,184,23,60,12,126,8,158,1,207,130,159,
130,231,155,236,189,235,4,216,206,228,3,37,192,15,74,129,8,6,101,100,239,247,10,80,9,176,141,9,219,145,176,221,8,91,139,106,68,
108,0,88,198,132,229,70,88,30,132,233,37,76,7,97,24,9,221,39,116,131,80,21,53,131,89,32,12,16,94,8,97,135,230,130,22,48,15,
180,130,54,16,81,49,231,2,48,31,92,8,46,2,29,224,114,176,26,92,1,214,128,181,100,247,33,247,114,40,121,115,165,221,47,166,158,
93,74,23,118,209,87,174,236,5,74,223,7,187,71,249,137,151,87,165,237,87,118,159,178,123,212,184,228,244,138,60,127,49,46,7,42,
109,93,140,201,65,149,183,78,249,20,169,177,16,109,40,86,227,113,88,249,11,253,62,229,31,86,254,197,106,140,190,83,105,247,125,
174,178,47,84,186,40,231,18,165,127,15,122,167,210,143,65,239,82,250,113,232,139,148,254,10,244,75,149,126,18,250,101,185,50,
49,169,139,149,94,0,125,169,210,253,121,246,105,121,122,8,250,114,165,135,243,236,23,230,233,93,121,250,242,188,50,123,243,236,
81,232,203,148,190,57,207,158,173,156,210,197,24,46,81,122,60,175,156,100,158,191,24,183,21,185,188,176,119,43,253,186,60,159,
125,121,250,254,42,123,29,181,168,241,124,159,210,15,192,222,163,244,131,208,87,42,253,27,208,123,149,254,0,244,85,74,63,154,
103,127,60,207,126,172,74,204,41,163,171,200,150,63,151,67,28,162,43,149,252,168,148,140,62,166,228,126,37,239,80,242,78,37,63,
161,252,63,79,98,175,181,208,231,164,172,161,159,146,216,119,65,58,174,228,75,82,86,211,203,36,214,29,167,219,164,172,161,187,
164,108,161,111,75,89,68,223,149,235,175,142,46,38,177,55,106,233,33,18,235,221,69,163,82,250,233,26,200,66,236,120,38,229,60,
122,132,196,122,172,147,207,69,202,94,132,221,243,168,236,151,253,92,130,250,146,82,150,146,169,158,175,21,107,71,217,253,216,
145,219,213,243,152,148,26,141,147,29,99,38,148,220,33,37,163,93,74,222,64,98,143,113,74,75,89,79,215,147,136,67,51,100,125,
101,136,10,223,148,178,157,238,151,178,130,254,141,196,62,108,166,126,37,31,38,17,171,154,164,255,116,68,144,143,72,217,64,79,
72,121,17,253,72,142,227,44,153,94,141,18,238,147,227,55,91,62,215,192,190,78,201,245,82,122,169,79,202,50,250,177,146,207,202,
241,156,41,253,131,24,201,168,148,97,218,32,101,25,109,86,82,151,50,34,239,73,65,212,180,77,74,151,188,83,5,85,255,130,136,122,
25,41,11,105,167,148,30,186,78,165,127,80,202,2,250,7,41,237,113,8,162,103,55,41,121,179,146,31,146,114,26,237,86,114,143,178,
239,149,178,138,62,172,228,45,74,222,42,229,116,218,39,101,27,125,70,201,3,82,6,233,30,181,126,190,170,228,97,37,255,89,165,127,
77,61,127,93,201,111,168,245,117,175,148,33,250,23,41,155,232,95,165,180,231,43,168,230,75,60,127,75,173,203,7,164,180,231,
47,136,40,127,68,202,185,244,152,146,143,75,217,72,63,144,178,156,254,83,202,57,244,148,122,254,161,242,59,166,228,211,42,253,
25,213,255,95,144,136,189,1,202,74,121,49,125,90,174,127,31,157,32,17,111,237,245,219,140,40,107,145,136,185,26,13,74,201,233,
75,36,226,110,9,125,138,236,51,142,200,142,207,34,46,137,179,98,57,228,199,85,144,174,81,107,184,89,249,137,244,40,210,15,169,
116,113,14,148,146,125,142,94,167,242,199,33,159,10,79,249,111,129,62,20,182,207,177,20,228,4,184,33,108,159,87,183,72,59,151,
250,137,38,177,47,113,31,12,20,81,202,127,53,108,1,218,236,247,200,179,169,0,79,34,255,171,240,25,16,245,185,202,169,158,123,
225,87,128,220,209,76,17,69,231,123,201,244,95,0,47,47,11,208,66,214,53,101,13,93,140,221,49,229,19,129,143,71,91,143,163,106,
125,19,163,123,163,183,147,230,184,232,234,106,218,144,241,80,135,86,73,1,109,51,180,156,127,52,91,68,219,253,77,216,231,94,199,
118,255,108,200,34,199,246,200,28,90,230,20,254,11,53,55,45,184,218,73,129,50,145,199,135,186,42,48,23,166,191,28,237,61,179,
142,240,83,226,252,214,228,152,248,154,237,187,200,64,192,171,250,90,143,185,72,133,66,24,237,1,191,143,6,202,138,101,191,153,
252,195,221,163,217,62,251,83,126,49,19,190,73,123,243,164,125,150,180,115,117,35,104,109,182,199,59,234,47,150,243,163,193,
46,234,189,184,217,62,143,163,161,98,180,82,220,62,208,195,58,209,238,106,212,236,155,244,91,116,94,191,26,233,231,67,92,16,61,
89,14,63,177,238,3,174,64,69,42,84,73,95,161,122,135,152,61,23,90,53,176,31,209,205,81,67,41,92,88,247,23,14,236,247,227,83,
150,19,37,124,128,14,146,199,209,225,184,138,102,122,196,51,110,228,212,127,160,4,90,63,37,104,243,103,16,105,67,125,180,9,239,
81,249,190,1,43,181,30,43,35,229,23,61,42,115,148,83,195,146,121,212,204,68,28,51,67,14,180,39,42,106,114,122,48,126,78,18,
37,186,17,179,2,154,233,215,145,203,235,168,118,126,159,2,205,13,67,109,20,112,15,22,148,209,234,2,151,59,80,21,44,40,146,154,
25,185,138,162,46,175,214,161,149,83,128,7,26,27,150,71,40,224,220,238,223,130,222,121,93,171,93,14,103,160,34,32,165,25,137,
211,199,29,24,3,63,230,22,229,206,116,216,173,223,79,225,47,20,59,194,39,193,219,224,77,240,51,112,28,83,46,239,150,83,175,93,
151,209,223,244,124,230,203,78,23,231,118,37,226,145,65,246,121,194,181,134,155,89,253,141,108,246,46,198,103,237,101,77,187,
153,61,223,226,117,143,90,7,125,161,18,185,143,197,250,19,207,247,230,214,77,164,139,234,52,49,155,246,254,58,162,214,101,223,
226,18,170,228,242,158,202,23,34,197,39,45,169,200,66,114,176,240,31,166,202,127,168,217,190,135,138,187,115,63,234,240,169,
58,196,235,241,102,123,255,7,252,37,147,235,242,25,213,158,13,254,82,89,15,87,235,248,120,179,125,55,52,253,23,138,157,131,59,
160,67,230,11,80,248,93,77,149,247,66,179,29,51,124,50,159,125,195,253,101,158,205,5,155,240,124,253,61,250,176,64,245,33,231,
255,187,247,240,239,152,236,179,221,142,83,231,104,135,123,214,217,54,95,158,205,161,234,170,152,101,223,151,3,76,68,222,129,8,
39,91,106,180,169,29,81,228,205,77,17,55,109,138,184,148,181,16,235,7,55,118,255,166,136,3,233,5,216,5,149,104,15,206,32,86,
170,250,43,198,190,113,150,29,131,207,221,254,232,210,0,165,214,47,38,199,186,240,59,226,179,129,38,231,189,229,188,121,230,175,
56,117,74,246,123,113,43,57,116,145,167,16,118,81,207,194,89,246,103,151,128,191,111,16,185,80,76,59,227,142,133,12,51,133,
241,107,113,139,207,78,34,197,244,23,33,94,123,88,106,221,37,212,156,9,255,198,138,84,209,120,129,135,194,111,152,254,185,104,
117,248,53,49,72,118,59,114,159,181,196,103,142,98,133,7,41,162,190,190,89,246,231,166,0,155,172,143,163,62,228,44,97,11,221,
133,168,167,16,109,243,240,64,195,5,23,184,201,90,60,141,198,63,233,97,225,183,77,156,25,216,247,188,131,191,126,42,167,155,161,
65,212,225,145,237,75,173,69,187,180,64,105,248,121,123,60,68,93,163,179,236,207,58,167,143,135,221,51,97,19,125,114,161,174,
212,250,78,140,100,192,31,254,173,125,183,20,175,107,242,230,185,144,156,178,188,189,106,124,173,43,167,83,116,60,191,212,14,
212,105,175,171,205,88,87,246,103,82,46,63,139,125,10,121,130,40,168,239,105,120,107,98,116,153,38,71,151,149,48,211,239,69,249,
158,66,211,47,206,133,34,247,179,183,159,162,185,236,119,114,116,194,111,245,61,131,157,194,76,236,191,25,136,172,166,95,68,104,
143,211,196,30,131,116,185,159,30,47,88,36,78,211,210,240,115,239,237,121,153,237,249,216,123,123,94,10,79,211,47,206,2,79,
81,160,116,65,205,28,10,212,53,4,91,17,19,219,113,151,9,84,95,116,164,142,68,41,162,140,175,137,50,66,1,33,89,192,185,220,233,
117,222,184,225,209,10,51,84,102,91,42,151,187,188,174,27,135,30,173,204,149,123,220,131,153,60,129,118,220,84,206,232,20,209,
55,111,127,201,195,10,22,122,166,209,95,219,131,217,52,116,42,191,61,127,79,75,68,73,225,159,255,237,45,88,164,90,208,244,255,
110,193,34,217,2,76,51,115,203,181,38,214,150,136,13,109,74,138,152,34,238,247,25,185,246,184,60,147,75,194,246,119,13,104,35,
78,64,55,86,127,181,246,36,5,202,27,150,227,4,116,14,186,112,2,202,211,236,42,186,192,225,101,11,81,178,15,251,46,252,46,248,83,
32,208,80,143,243,79,19,231,95,1,206,184,213,14,174,137,115,111,59,15,191,81,204,195,255,3,94,3,175,136,245,94,138,182,137,
251,161,87,68,34,62,171,178,105,154,214,16,174,111,153,61,39,47,6,46,12,79,237,13,77,89,151,132,237,123,94,7,119,99,127,153,161,
43,225,225,163,192,226,240,31,69,255,236,56,185,50,108,127,55,98,250,219,228,141,174,154,127,18,158,28,177,211,139,79,38,211,
169,3,145,195,244,199,176,67,61,108,17,43,195,104,155,145,153,20,65,143,47,146,254,102,100,6,249,29,209,118,63,238,110,13,178,
252,92,74,128,155,145,90,242,115,59,173,81,140,239,219,246,247,186,249,175,187,207,120,126,224,140,103,209,167,114,178,207,183,
82,180,130,41,155,253,153,201,150,5,74,86,41,89,163,252,155,17,69,197,115,88,61,135,49,155,14,202,221,245,236,243,144,171,185,
206,157,155,92,205,117,238,188,180,117,183,252,206,133,75,90,242,236,66,22,203,103,135,170,219,165,210,92,240,227,202,230,86,
178,80,73,175,202,235,67,139,236,249,85,231,183,202,19,84,243,39,62,35,137,244,22,213,198,150,188,118,11,230,41,217,170,242,51,
117,183,16,178,100,210,86,162,234,178,243,249,39,235,82,247,90,209,214,174,132,153,200,94,74,149,203,172,209,148,101,26,102,
118,173,97,164,55,38,140,137,214,173,250,184,78,172,155,120,119,15,177,30,226,61,16,43,137,175,236,165,170,85,198,206,65,75,79,
199,151,39,50,163,137,76,166,55,145,201,26,166,145,38,214,75,188,23,174,189,189,164,245,226,173,178,87,55,227,105,43,17,111,
211,83,169,182,37,177,108,98,60,145,221,217,73,23,158,110,79,165,146,137,152,158,77,88,102,99,206,167,55,49,100,196,118,198,146,
198,50,61,153,28,212,99,219,50,157,52,253,124,185,242,147,98,150,137,182,100,219,150,9,185,35,155,159,52,156,214,83,35,137,
88,166,109,153,110,142,235,40,112,198,57,146,172,164,149,238,78,36,179,70,250,252,233,171,245,108,58,177,163,147,102,255,197,
244,211,138,154,118,182,235,90,61,97,162,125,85,103,167,172,55,98,72,40,155,76,176,50,109,75,199,204,120,210,232,164,242,124,
99,207,210,132,25,23,165,79,149,49,142,153,107,195,244,172,24,55,68,225,213,167,39,172,182,196,112,169,180,217,167,167,137,57,
111,92,99,118,91,177,177,204,178,17,221,28,54,114,211,154,223,148,73,215,252,46,77,26,47,79,91,99,169,78,186,248,236,148,104,
218,48,214,12,102,140,244,184,145,70,45,151,39,173,65,61,217,171,239,180,198,178,83,213,204,252,203,249,58,169,245,116,135,132,
153,26,203,142,26,217,17,43,222,182,84,207,24,61,226,25,19,111,98,252,228,178,104,58,191,255,138,120,34,107,165,123,204,33,
171,147,230,156,223,237,172,34,231,189,135,239,106,169,175,214,77,125,88,180,184,187,55,102,141,182,165,71,51,201,182,173,99,49,
163,237,172,109,214,120,158,189,212,120,122,207,23,254,189,229,116,82,221,123,101,237,164,250,222,184,158,28,79,108,107,211,
77,211,202,202,61,213,182,194,140,37,173,76,194,28,94,150,212,51,114,179,156,237,211,131,113,73,171,244,186,115,164,175,54,70,7,
149,131,145,17,43,70,196,148,182,36,22,87,27,150,88,186,207,216,62,102,152,49,44,235,210,252,20,187,188,250,60,83,79,50,105,
12,235,201,37,177,152,145,201,172,216,17,51,82,246,100,52,158,195,39,61,60,54,138,206,229,121,149,229,123,33,42,12,219,163,50,
101,188,194,234,27,139,141,216,51,151,151,47,144,231,178,102,112,171,220,148,181,121,182,62,35,54,150,70,172,58,79,150,62,4,
1,115,88,172,152,41,91,218,24,74,162,28,52,99,220,178,99,87,84,79,15,27,249,173,173,62,135,187,221,52,220,138,163,155,214,174,
32,111,254,210,32,182,145,248,198,30,114,110,236,193,11,234,74,114,109,92,217,211,221,189,146,28,144,61,226,93,132,227,141,43,
251,145,40,148,222,149,226,77,106,253,72,237,237,71,80,223,216,143,92,253,178,4,214,79,90,191,200,135,183,94,161,34,166,247,11,
69,4,246,1,28,7,3,61,20,24,56,123,190,202,6,206,49,92,30,93,78,89,99,36,18,153,212,219,243,244,11,242,244,249,121,250,133,208,
139,108,189,59,169,15,103,200,165,203,253,39,140,66,246,234,131,70,146,10,116,117,90,208,116,61,30,63,119,84,33,54,72,165,226,
0,89,58,150,205,90,230,218,52,138,52,226,228,26,180,240,56,10,41,3,43,185,98,242,76,32,119,76,134,190,56,57,113,246,232,105,
42,138,89,113,99,173,133,72,189,36,43,30,38,163,59,249,228,67,52,173,155,153,33,43,61,74,197,226,228,65,72,207,72,111,20,100,31,
64,40,200,26,195,179,35,158,24,26,34,102,144,211,16,225,151,188,67,83,129,54,78,133,88,3,75,236,30,86,8,117,234,108,83,231,
24,21,195,44,246,12,186,108,164,51,84,32,30,197,192,147,71,104,202,201,43,150,146,40,62,154,24,53,100,161,239,51,18,195,35,89,
42,131,218,171,86,220,26,179,47,134,5,100,202,116,123,101,81,9,84,217,110,236,86,217,92,239,148,161,39,78,110,60,173,215,39,
174,204,41,155,168,72,40,150,149,21,241,131,252,120,232,219,137,241,30,237,195,154,76,196,12,242,193,178,193,76,136,193,19,141,
150,245,159,25,206,101,151,54,38,50,137,193,68,82,204,161,200,243,126,28,104,214,68,212,218,134,230,5,39,159,165,83,210,64,
100,75,37,245,157,221,105,29,157,115,32,245,74,249,190,137,216,8,149,96,32,49,143,24,184,181,250,152,152,96,255,164,97,189,145,
65,68,152,180,44,157,92,11,84,108,91,16,57,151,91,19,88,91,147,143,27,82,84,62,249,32,163,234,251,18,241,56,218,164,170,89,
109,161,14,153,231,52,67,90,31,206,149,41,13,40,70,149,41,207,122,42,24,209,51,242,124,165,170,145,68,220,232,179,134,178,242,
204,232,78,91,163,118,79,225,2,199,168,152,75,199,136,149,201,18,75,144,7,115,185,70,6,135,12,105,137,209,81,42,17,87,182,132,
158,92,166,167,50,171,49,194,84,172,12,125,70,114,133,25,159,76,199,99,95,86,79,103,169,80,158,82,209,157,41,131,188,82,221,98,
159,88,228,74,32,14,109,51,80,69,166,199,204,100,117,196,98,42,72,100,214,164,116,4,102,100,203,168,145,39,247,54,99,231,50,
81,85,213,182,243,92,253,138,115,9,125,35,98,92,156,73,185,67,139,49,95,70,90,212,124,5,142,28,114,36,141,161,44,185,146,134,57,
156,29,33,151,106,5,51,201,97,138,57,117,155,198,196,21,114,114,173,100,124,68,190,79,80,169,101,230,46,133,203,210,134,158,
197,76,150,77,153,150,27,153,108,218,218,41,166,119,202,168,150,64,94,206,220,26,168,153,50,245,233,227,70,174,211,24,166,172,
145,239,47,199,237,244,34,250,178,86,42,5,83,21,246,164,108,199,25,87,3,114,89,38,86,192,4,21,91,249,151,40,242,89,167,197,37,
42,180,204,220,130,43,150,234,234,177,100,54,145,18,131,44,31,177,104,10,68,128,147,206,240,232,75,92,99,228,194,132,23,231,133,
133,163,74,238,113,84,104,207,147,219,150,248,48,137,44,75,178,89,132,7,103,74,46,56,79,74,79,195,83,110,82,103,74,198,37,
150,166,250,180,49,44,166,45,125,254,203,54,5,211,198,168,53,110,216,45,95,99,158,17,83,157,105,25,85,180,140,145,37,95,70,196,
159,201,171,46,121,241,44,251,175,139,133,83,149,255,212,99,183,94,174,89,145,45,239,194,36,179,245,230,22,11,77,195,211,57,
239,162,84,145,201,197,154,13,137,188,224,81,115,78,179,184,234,232,136,219,25,59,250,200,181,85,156,57,45,234,120,114,143,73,
187,77,239,79,36,147,87,88,89,57,147,222,12,214,114,46,2,32,35,158,38,247,44,156,197,26,177,219,133,115,30,201,88,32,83,143,
211,50,118,107,122,166,234,82,61,117,100,71,18,56,208,196,123,99,68,201,118,88,69,200,214,80,6,84,49,4,5,99,217,161,14,25,60,
217,56,57,199,245,164,152,105,41,214,12,145,67,92,62,169,68,188,231,175,143,66,97,136,90,27,50,6,249,199,207,12,183,158,241,169,
94,179,9,98,59,136,239,136,128,118,98,59,233,7,156,145,219,215,223,69,207,224,19,111,203,128,198,31,98,197,123,52,118,148,149,
205,212,40,193,91,119,252,126,107,23,43,45,77,116,241,76,109,23,29,225,156,94,102,110,31,191,100,19,191,124,98,30,125,139,179,
87,241,120,150,60,138,194,124,247,209,35,182,232,208,134,31,100,183,51,119,11,127,154,58,249,27,108,130,63,252,193,137,189,140,
59,61,75,230,117,181,118,117,93,58,160,81,220,115,157,198,140,214,174,67,117,154,246,101,54,151,85,85,68,102,104,252,139,140,
179,210,42,39,231,235,106,157,228,100,78,205,229,225,115,15,58,61,46,114,49,23,119,105,115,230,240,241,22,39,159,195,51,45,52,
223,174,122,62,127,149,189,38,148,63,136,142,221,18,164,52,196,42,58,166,241,87,216,175,133,253,102,97,167,71,53,241,254,59,
141,247,111,129,124,83,99,253,16,63,147,2,121,110,112,8,229,214,32,189,166,28,254,100,59,188,108,139,63,115,246,93,230,174,93,
181,106,94,255,170,254,86,186,150,93,45,115,29,208,248,139,236,35,168,227,214,154,121,116,19,231,119,177,109,238,218,61,60,80,
203,83,181,188,164,147,143,29,220,204,119,174,164,67,92,187,139,141,32,137,23,239,230,227,181,119,14,108,221,163,209,11,140,247,
211,1,59,143,111,79,40,120,39,221,161,57,245,47,177,231,216,35,236,3,168,114,159,230,248,21,187,137,221,195,190,130,242,23,
237,233,167,143,51,233,201,31,167,90,126,228,250,218,85,90,225,149,188,87,115,255,156,105,252,97,218,196,23,183,176,178,146,136
,141,191,87,43,58,196,248,162,46,205,251,31,172,109,17,99,154,231,99,140,207,99,193,226,203,156,30,167,183,221,89,180,213,229,
105,101,101,149,252,218,206,46,151,119,17,171,173,16,246,211,141,188,155,213,250,232,171,26,251,37,26,16,210,216,127,97,146,2,
149,220,219,194,71,107,177,84,182,134,103,56,73,200,230,6,39,189,20,153,67,255,164,177,187,197,112,255,64,99,155,221,190,68,
144,126,197,216,243,48,124,79,163,253,172,186,117,235,170,29,87,79,223,67,188,150,189,200,166,213,240,25,124,163,131,31,98,85,
11,108,67,80,26,2,48,20,242,153,48,4,217,180,234,156,82,67,156,49,15,103,183,133,66,187,118,57,142,86,213,177,151,171,72,115,
145,247,182,16,86,11,175,191,97,151,227,240,116,182,55,244,156,120,59,41,222,246,85,51,126,16,60,89,77,142,146,233,37,156,201,
191,48,28,79,86,35,121,255,12,188,29,158,225,184,145,83,33,96,127,5,65,48,151,125,111,6,99,39,193,109,51,203,217,193,153,140,
125,7,156,0,39,193,222,16,99,15,128,87,192,159,192,109,117,140,29,4,239,128,195,245,240,3,199,26,144,167,145,57,222,1,251,154,
24,59,218,228,96,123,231,48,182,127,46,103,119,131,39,231,218,223,221,228,190,19,203,201,220,111,43,197,119,58,185,223,87,138,
239,128,114,191,177,116,208,212,239,44,197,119,72,185,223,90,230,190,167,18,191,183,212,252,182,46,190,155,99,33,251,183,69,71,
161,187,66,182,93,252,159,154,249,237,239,209,228,255,174,67,118,189,226,247,153,154,242,23,255,83,118,132,236,114,197,255,
161,73,229,149,255,223,246,219,109,21,191,5,253,63,31,222,169,74,68,42,0,0,0,0};

//==============================================================================
#if JUCE_PUSH_NOTIFICATIONS && JUCE_MODULE_AVAILABLE_juce_gui_extra
 // Returns true if the intent was handled.
 extern bool juce_handleNotificationIntent (void*);
 extern void juce_firebaseDeviceNotificationsTokenRefreshed (void*);
 extern void juce_firebaseRemoteNotificationReceived (void*);
 extern void juce_firebaseRemoteMessagesDeleted();
 extern void juce_firebaseRemoteMessageSent(void*);
 extern void juce_firebaseRemoteMessageSendError (void*, void*);
#endif

//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 METHOD (create,                      "<init>",                      "(II)V")

DECLARE_JNI_CLASS (AndroidLayoutParams, "android/view/ViewGroup$LayoutParams")
#undef JNI_CLASS_MEMBERS

//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 METHOD (addView,       "addView",             "(Landroid/view/View;Landroid/view/ViewGroup$LayoutParams;)V") \
 METHOD (removeView, "removeView", "(Landroid/view/View;)V") \
 METHOD (updateViewLayout, "updateViewLayout", "(Landroid/view/View;Landroid/view/ViewGroup$LayoutParams;)V")

DECLARE_JNI_CLASS (AndroidViewManager, "android/view/ViewManager")
#undef JNI_CLASS_MEMBERS

//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 METHOD (create,           "<init>",             "(IIIIIII)V") \
 FIELD  (gravity,          "gravity",            "I") \
 FIELD  (windowAnimations, "windowAnimations",   "I")

DECLARE_JNI_CLASS (AndroidWindowManagerLayoutParams, "android/view/WindowManager$LayoutParams")
#undef JNI_CLASS_MEMBERS

//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 METHOD (getDecorView, "getDecorView",       "()Landroid/view/View;") \
 METHOD (setFlags,     "setFlags",           "(II)V") \
 METHOD (clearFlags,   "clearFlags",         "(I)V")

DECLARE_JNI_CLASS (AndroidWindow, "android/view/Window")
#undef JNI_CLASS_MEMBERS

namespace
{
    enum
    {
        SYSTEM_UI_FLAG_VISIBLE = 0,
        SYSTEM_UI_FLAG_LOW_PROFILE = 1,
        SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2,
        SYSTEM_UI_FLAG_FULLSCREEN = 4,
        SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512,
        SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024,
        SYSTEM_UI_FLAG_IMMERSIVE = 2048,
        SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096
    };

    constexpr int fullScreenFlags = SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;

    constexpr int FLAG_NOT_FOCUSABLE = 0x8;
}

//==============================================================================
class AndroidComponentPeer  : public ComponentPeer,
                              private Timer
{
public:
    AndroidComponentPeer (Component& comp, int windowStyleFlags, void* nativeViewHandle)
        : ComponentPeer (comp, windowStyleFlags),
          fullScreen (false),
          navBarsHidden (false),
          sizeAllocated (0),
          scale ((float) Desktop::getInstance().getDisplays().getMainDisplay().scale)
    {
        auto* env = getEnv();

        // NB: must not put this in the initialiser list, as it invokes a callback,
        // which will fail if the peer is only half-constructed.
        view = GlobalRef (LocalRef<jobject> (env->NewObject (ComponentPeerView, ComponentPeerView.create,
                                                             getAppContext().get(), (jboolean) component.isOpaque(),
                                                             (jlong) this)));

        if (nativeViewHandle != nullptr)
        {
            viewGroupIsWindow = false;

            // we don't know if the user is holding on to a local ref to this, so
            // explicitly create a new one
            auto nativeView = LocalRef<jobject>(env->NewLocalRef(static_cast<jobject> (nativeViewHandle)));

            if (env->IsInstanceOf (nativeView.get(), AndroidActivity))
            {
                viewGroup = GlobalRef (nativeView);
                env->CallVoidMethod (viewGroup.get(), AndroidActivity.setContentView, view.get());
            }
            else if (env->IsInstanceOf (nativeView.get(), AndroidViewGroup))
            {
                viewGroup = GlobalRef (nativeView);
                LocalRef<jobject> layoutParams (env->NewObject (AndroidLayoutParams, AndroidLayoutParams.create, -2, -2));

                env->CallVoidMethod (view.get(), AndroidView.setLayoutParams, layoutParams.get());
                env->CallVoidMethod ((jobject) viewGroup.get(), AndroidViewGroup.addView, view.get());
            }
            else
            {
                // the native handle you passed as a second argument to Component::addToDesktop must
                // either be an Activity or a ViewGroup
                jassertfalse;
            }
        }
        else
        {
            viewGroupIsWindow = true;

            LocalRef<jobject> viewLayoutParams (env->NewObject (AndroidLayoutParams, AndroidLayoutParams.create, -2, -2));

            env->CallVoidMethod (view.get(), AndroidView.setLayoutParams, viewLayoutParams.get());

            Rectangle<int> physicalBounds = comp.getBoundsInParent() * scale;

            view.callVoidMethod (AndroidView.layout,
                                 physicalBounds.getX(), physicalBounds.getY(), physicalBounds.getRight(), physicalBounds.getBottom());

            LocalRef<jobject> windowLayoutParams (env->NewObject (AndroidWindowManagerLayoutParams, AndroidWindowManagerLayoutParams.create,
                                                                  physicalBounds.getWidth(), physicalBounds.getHeight(),
                                                                  physicalBounds.getX(), physicalBounds.getY(),
                                                                  TYPE_APPLICATION, FLAG_NOT_TOUCH_MODAL | FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_NO_LIMITS | FLAG_NOT_FOCUSABLE,
                                                                  component.isOpaque() ? PIXEL_FORMAT_OPAQUE : PIXEL_FORMAT_TRANSPARENT));
            env->SetIntField (windowLayoutParams.get(), AndroidWindowManagerLayoutParams.gravity, GRAVITY_LEFT | GRAVITY_TOP);
            env->SetIntField (windowLayoutParams.get(), AndroidWindowManagerLayoutParams.windowAnimations, 0x01030000 /* android.R.style.Animation */);

            if (Desktop::getInstance().getKioskModeComponent() != nullptr)
                setNavBarsHidden (true);

            LocalRef<jobject> activity (getCurrentActivity());

            if (activity == nullptr)
                activity = getMainActivity();

            viewGroup = GlobalRef (LocalRef<jobject> (env->CallObjectMethod (activity.get(), AndroidContext.getSystemService, javaString ("window").get())));
            env->CallVoidMethod (viewGroup.get(), AndroidViewManager.addView, view.get(), windowLayoutParams.get());
        }

        if (isFocused())
            handleFocusGain();
    }

    ~AndroidComponentPeer() override
    {
        auto* env = getEnv();

        env->CallVoidMethod (view, ComponentPeerView.clear);
        frontWindow = nullptr;

        if (MessageManager::getInstance()->isThisTheMessageThread())
        {
            if (env->IsInstanceOf (viewGroup.get(), AndroidActivity))
                env->CallVoidMethod (viewGroup.get(), AndroidActivity.setContentView, nullptr);
            else
                env->CallVoidMethod (viewGroup.get(), AndroidViewManager.removeView, view.get());
        }
        else
        {
            struct ViewDeleter  : public CallbackMessage
            {
                ViewDeleter (const GlobalRef& view_, const GlobalRef& viewGroup_) : view (view_), group (viewGroup_) {}

                void messageCallback() override
                {
                    auto* callbackEnv = getEnv();

                    if (callbackEnv->IsInstanceOf (group.get(), AndroidActivity))
                        callbackEnv->CallVoidMethod (group.get(), AndroidActivity.setContentView, nullptr);
                    else
                        callbackEnv->CallVoidMethod (group.get(), AndroidViewManager.removeView, view.get());
                }

            private:
                GlobalRef view, group;
            };

            (new ViewDeleter (view, viewGroup))->post();
        }
    }

    void* getNativeHandle() const override
    {
        return (void*) view.get();
    }

    void setVisible (bool shouldBeVisible) override
    {
        if (MessageManager::getInstance()->isThisTheMessageThread())
        {
            view.callVoidMethod (ComponentPeerView.setVisible, shouldBeVisible);
        }
        else
        {
            struct VisibilityChanger  : public CallbackMessage
            {
                VisibilityChanger (const GlobalRef& view_, bool shouldBeVisible_)
                    : view (view_), shouldBeVisible (shouldBeVisible_)
                {}

                void messageCallback() override
                {
                    view.callVoidMethod (ComponentPeerView.setVisible, shouldBeVisible);
                }

                GlobalRef view;
                bool shouldBeVisible;
            };

            (new VisibilityChanger (view, shouldBeVisible))->post();
        }
    }

    void setTitle (const String& title) override
    {
        view.callVoidMethod (ComponentPeerView.setViewName, javaString (title).get());
    }

    void setBounds (const Rectangle<int>& userRect, bool isNowFullScreen) override
    {
        Rectangle<int> r = (userRect.toFloat() * scale).toNearestInt();

        if (MessageManager::getInstance()->isThisTheMessageThread())
        {
            auto* env = getEnv();

            fullScreen = isNowFullScreen;

            {
                view.callVoidMethod (AndroidView.layout,
                                     r.getX(), r.getY(), r.getRight(), r.getBottom());

                if (viewGroup != nullptr && viewGroupIsWindow)
                {
                    LocalRef<jobject> windowLayoutParams (env->NewObject (AndroidWindowManagerLayoutParams, AndroidWindowManagerLayoutParams.create,
                                                                          r.getWidth(), r.getHeight(),
                                                                          r.getX(), r.getY(),
                                                                          TYPE_APPLICATION, FLAG_NOT_TOUCH_MODAL | FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_NO_LIMITS,
                                                                          component.isOpaque() ? PIXEL_FORMAT_OPAQUE : PIXEL_FORMAT_TRANSPARENT));
                    env->SetIntField (windowLayoutParams.get(), AndroidWindowManagerLayoutParams.gravity, 0x3 /* LEFT */ | 0x30 /* TOP */);
                    env->CallVoidMethod (viewGroup.get(), AndroidViewManager.updateViewLayout, view.get(), windowLayoutParams.get());
                }
            }
        }
        else
        {
            class ViewMover  : public CallbackMessage
            {
            public:
                ViewMover (const GlobalRef& v, Rectangle<int> boundsToUse)  : view (v), bounds (boundsToUse) {}

                void messageCallback() override
                {
                    view.callVoidMethod (AndroidView.layout,
                                         bounds.getX(), bounds.getY(), bounds.getRight(), bounds.getBottom());
                }

            private:
                GlobalRef view;
                Rectangle<int> bounds;
            };

            (new ViewMover (view, r))->post();
        }
    }

    Rectangle<int> getBounds() const override
    {
        Rectangle<int> r (view.callIntMethod (AndroidView.getLeft),
                          view.callIntMethod (AndroidView.getTop),
                          view.callIntMethod (AndroidView.getWidth),
                          view.callIntMethod (AndroidView.getHeight));

        return r / scale;
    }

    void handleScreenSizeChange() override
    {
        ComponentPeer::handleScreenSizeChange();

        if (isFullScreen())
            setFullScreen (true);
    }

    Point<int> getScreenPosition() const
    {
        auto* env = getEnv();

        LocalRef<jintArray> position (env->NewIntArray (2));
        env->CallVoidMethod (view.get(), AndroidView.getLocationOnScreen, position.get());

        jint* const screenPosition = env->GetIntArrayElements (position.get(), nullptr);
        Point<int> pos (screenPosition[0], screenPosition[1]);
        env->ReleaseIntArrayElements (position.get(), screenPosition, 0);

        return pos;
    }

    Point<float> localToGlobal (Point<float> relativePosition) override
    {
        return relativePosition + (getScreenPosition().toFloat() / scale);
    }

    using ComponentPeer::localToGlobal;

    Point<float> globalToLocal (Point<float> screenPosition) override
    {
        return screenPosition - (getScreenPosition().toFloat() / scale);
    }

    using ComponentPeer::globalToLocal;

    void setMinimised (bool /*shouldBeMinimised*/) override
    {
        // n/a
    }

    bool isMinimised() const override
    {
        return false;
    }

    bool shouldNavBarsBeHidden (bool shouldBeFullScreen) const
    {
        if (shouldBeFullScreen)
            if (Component* kiosk = Desktop::getInstance().getKioskModeComponent())
                if (kiosk->getPeer() == this)
                    return true;

        return false;
    }

    void setNavBarsHidden (bool hidden)
    {
        view.callVoidMethod (ComponentPeerView.setSystemUiVisibilityCompat,
                             hidden ? (jint) (fullScreenFlags)
                                    : (jint) (SYSTEM_UI_FLAG_VISIBLE));

        navBarsHidden = hidden;
    }

    void setFullScreen (bool shouldBeFullScreen) override
    {
        // updating the nav bar visibility is a bit odd on Android - need to wait for
        if (shouldNavBarsBeHidden (shouldBeFullScreen))
        {
            if (! isTimerRunning())
            {
                startTimer (500);
            }
        }
        else
        {
            setNavBarsHidden (false);
        }

        Rectangle<int> r (shouldBeFullScreen ? Desktop::getInstance().getDisplays().getMainDisplay().userArea
                                             : lastNonFullscreenBounds);

        if ((! shouldBeFullScreen) && r.isEmpty())
            r = getBounds();

        // (can't call the component's setBounds method because that'll reset our fullscreen flag)
        if (! r.isEmpty())
            setBounds (r, shouldBeFullScreen);

        component.repaint();
    }

    bool isFullScreen() const override
    {
        return fullScreen;
    }

    void timerCallback() override
    {
        setNavBarsHidden (shouldNavBarsBeHidden (fullScreen));
        setFullScreen (fullScreen);
        stopTimer();
    }

    void setIcon (const Image& /*newIcon*/) override
    {
        // n/a
    }

    bool contains (Point<int> localPos, bool trueIfInAChildWindow) const override
    {
        return isPositiveAndBelow (localPos.x, component.getWidth())
            && isPositiveAndBelow (localPos.y, component.getHeight())
            && ((! trueIfInAChildWindow) || view.callBooleanMethod (ComponentPeerView.containsPoint,
                                                                    localPos.x * scale,
                                                                    localPos.y * scale));
    }

    BorderSize<int> getFrameSize() const override
    {
        // TODO
        return BorderSize<int>();
    }

    bool setAlwaysOnTop (bool /*alwaysOnTop*/) override
    {
        // TODO
        return false;
    }

    void toFront (bool makeActive) override
    {
        // Avoid calling bringToFront excessively: it's very slow
        if (frontWindow != this)
        {
            view.callVoidMethod (AndroidView.bringToFront);

            frontWindow = this;
        }

        if (makeActive)
            grabFocus();

        handleBroughtToFront();
    }

    void toBehind (ComponentPeer*) override
    {
        // TODO
    }

    //==============================================================================
    void handleMouseDownCallback (int index, Point<float> sysPos, int64 time)
    {
        lastMousePos = sysPos / scale;
        Point<float> pos = globalToLocal (lastMousePos);

        // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
        handleMouseEvent (MouseInputSource::InputSourceType::touch, pos, ModifierKeys::currentModifiers.withoutMouseButtons(),
                          MouseInputSource::invalidPressure, MouseInputSource::invalidOrientation, time, {}, index);

        if (isValidPeer (this))
            handleMouseDragCallback (index, sysPos, time);
    }

    void handleMouseDragCallback (int index, Point<float> sysPos, int64 time)
    {
        lastMousePos = sysPos / scale;
        Point<float> pos = globalToLocal (lastMousePos);

        jassert (index < 64);
        touchesDown = (touchesDown | (1 << (index & 63)));
        ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
        handleMouseEvent (MouseInputSource::InputSourceType::touch, pos, ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier),
                          MouseInputSource::invalidPressure, MouseInputSource::invalidOrientation, time, {}, index);
    }

    void handleMouseUpCallback (int index, Point<float> sysPos, int64 time)
    {
        lastMousePos = sysPos / scale;
        Point<float> pos = globalToLocal (lastMousePos);

        jassert (index < 64);
        touchesDown = (touchesDown & ~(1 << (index & 63)));

        if (touchesDown == 0)
            ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons();

        handleMouseEvent (MouseInputSource::InputSourceType::touch, pos, ModifierKeys::currentModifiers.withoutMouseButtons(), MouseInputSource::invalidPressure,
                          MouseInputSource::invalidOrientation, time, {}, index);
    }

    void handleKeyDownCallback (int k, int kc)
    {
        handleKeyPress (k, static_cast<juce_wchar> (kc));
    }

    void handleKeyUpCallback (int /*k*/, int /*kc*/)
    {
    }

    void handleBackButtonCallback()
    {
        bool handled = false;

        if (auto* app = JUCEApplicationBase::getInstance())
            handled = app->backButtonPressed();

        if (Component* kiosk = Desktop::getInstance().getKioskModeComponent())
            if (kiosk->getPeer() == this)
                setNavBarsHidden (navBarsHidden);

        if (! handled)
        {
            auto* env = getEnv();
            LocalRef<jobject> activity (getCurrentActivity());

            if (activity != nullptr)
            {
                jmethodID finishMethod = env->GetMethodID (AndroidActivity, "finish", "()V");

                if (finishMethod != nullptr)
                    env->CallVoidMethod (activity.get(), finishMethod);
            }
        }

    }

    void handleKeyboardHiddenCallback()
    {
        Component::unfocusAllComponents();
    }

    void handleAppPausedCallback() {}

    void handleAppResumedCallback()
    {
        if (Component* kiosk = Desktop::getInstance().getKioskModeComponent())
            if (kiosk->getPeer() == this)
                setNavBarsHidden (navBarsHidden);
    }

    //==============================================================================
    bool isFocused() const override
    {
        if (view != nullptr)
            return view.callBooleanMethod (AndroidView.hasFocus);

        return false;
    }

    void grabFocus() override
    {
        if (view != nullptr)
            view.callBooleanMethod (AndroidView.requestFocus);
    }

    void handleFocusChangeCallback (bool hasFocus)
    {
        if (isFullScreen())
            setFullScreen (true);

        if (hasFocus)
            handleFocusGain();
        else
            handleFocusLoss();
    }

    static const char* getVirtualKeyboardType (TextInputTarget::VirtualKeyboardType type) noexcept
    {
        switch (type)
        {
            case TextInputTarget::textKeyboard:          return "text";
            case TextInputTarget::numericKeyboard:       return "number";
            case TextInputTarget::decimalKeyboard:       return "numberDecimal";
            case TextInputTarget::urlKeyboard:           return "textUri";
            case TextInputTarget::emailAddressKeyboard:  return "textEmailAddress";
            case TextInputTarget::phoneNumberKeyboard:   return "phone";
            default:                                     jassertfalse; break;
        }

        return "text";
    }

    void textInputRequired (Point<int>, TextInputTarget& target) override
    {
        view.callVoidMethod (ComponentPeerView.showKeyboard,
                             javaString (getVirtualKeyboardType (target.getKeyboardType())).get());
    }

    void dismissPendingTextInput() override
    {
        view.callVoidMethod (ComponentPeerView.showKeyboard, javaString ("").get());

        // updating the nav bar visibility is a bit odd on Android - need to wait for
        if (! isTimerRunning())
            hideNavBarDelayed();
     }

    void hideNavBarDelayed()
    {
        startTimer (500);
    }

    //==============================================================================
    void handlePaintCallback (jobject canvas, jobject paint)
    {
        auto* env = getEnv();

        jobject rect = env->CallObjectMethod (canvas, AndroidCanvas.getClipBounds);
        const int left   = env->GetIntField (rect, AndroidRect.left);
        const int top    = env->GetIntField (rect, AndroidRect.top);
        const int right  = env->GetIntField (rect, AndroidRect.right);
        const int bottom = env->GetIntField (rect, AndroidRect.bottom);
        env->DeleteLocalRef (rect);

        const Rectangle<int> clip (left, top, right - left, bottom - top);

        const int sizeNeeded = clip.getWidth() * clip.getHeight();
        if (sizeAllocated < sizeNeeded)
        {
            buffer.clear();
            sizeAllocated = sizeNeeded;
            buffer = GlobalRef (LocalRef<jobject> ((jobject) env->NewIntArray (sizeNeeded)));
        }
        else if (sizeNeeded == 0)
        {
            return;
        }

        if (jint* dest = env->GetIntArrayElements ((jintArray) buffer.get(), nullptr))
        {
            {
                Image temp (new PreallocatedImage (clip.getWidth(), clip.getHeight(),
                                                   dest, ! component.isOpaque()));

                {
                    LowLevelGraphicsSoftwareRenderer g (temp);
                    g.setOrigin (-clip.getPosition());
                    g.addTransform (AffineTransform::scale (scale));
                    handlePaint (g);
                }
            }

            env->ReleaseIntArrayElements ((jintArray) buffer.get(), dest, 0);

            env->CallVoidMethod (canvas, AndroidCanvas.drawBitmap, (jintArray) buffer.get(), 0, clip.getWidth(),
                                 (jfloat) clip.getX(), (jfloat) clip.getY(),
                                 clip.getWidth(), clip.getHeight(), true, paint);
        }
    }

    void repaint (const Rectangle<int>& userArea) override
    {
        Rectangle<int> area = userArea * scale;

        if (MessageManager::getInstance()->isThisTheMessageThread())
        {
            view.callVoidMethod (AndroidView.invalidate, area.getX(), area.getY(), area.getRight(), area.getBottom());
        }
        else
        {
            struct ViewRepainter  : public CallbackMessage
            {
                ViewRepainter (const GlobalRef& view_, const Rectangle<int>& area_)
                    : view (view_), area (area_) {}

                void messageCallback() override
                {
                    view.callVoidMethod (AndroidView.invalidate, area.getX(), area.getY(),
                                         area.getRight(), area.getBottom());
                }

            private:
                GlobalRef view;
                const Rectangle<int> area;
            };

            (new ViewRepainter (view, area))->post();
        }
    }

    void performAnyPendingRepaintsNow() override
    {
        // TODO
    }

    void setAlpha (float /*newAlpha*/) override
    {
        // TODO
    }

    StringArray getAvailableRenderingEngines() override
    {
        return StringArray ("Software Renderer");
    }

    //==============================================================================
    static Point<float> lastMousePos;
    static int64 touchesDown;

    //==============================================================================
    struct StartupActivityCallbackListener : ActivityLifecycleCallbacks
    {
        void onActivityStarted (jobject /*activity*/) override
        {
            auto* env = getEnv();
            LocalRef<jobject> appContext (getAppContext());

            if (appContext.get() != nullptr)
            {

                env->CallVoidMethod (appContext.get(),
                                     AndroidApplication.unregisterActivityLifecycleCallbacks,
                                     activityCallbackListener.get());
                clear();
                activityCallbackListener.clear();

                const_cast<Displays &> (Desktop::getInstance().getDisplays()).refresh();
            }
        }
    };

private:
    //==============================================================================
    GlobalRef view, viewGroup;
    bool viewGroupIsWindow = false;
    GlobalRef buffer;
    bool fullScreen;
    bool navBarsHidden;
    int sizeAllocated;
    float scale;

    //==============================================================================
    #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
     METHOD   (create,                      "<init>",                      "(Landroid/content/Context;ZJ)V") \
     METHOD   (clear,                       "clear",                       "()V") \
     METHOD   (setViewName,                 "setViewName",                 "(Ljava/lang/String;)V") \
     METHOD   (setVisible,                  "setVisible",                  "(Z)V") \
     METHOD   (isVisible,                   "isVisible",                   "()Z") \
     METHOD   (containsPoint,               "containsPoint",               "(II)Z") \
     METHOD   (showKeyboard,                "showKeyboard",                "(Ljava/lang/String;)V") \
     METHOD   (setSystemUiVisibilityCompat, "setSystemUiVisibilityCompat", "(I)V") \
     CALLBACK (handlePaintJni,              "handlePaint",                 "(JLandroid/graphics/Canvas;Landroid/graphics/Paint;)V") \
     CALLBACK (handleMouseDownJni,          "handleMouseDown",             "(JIFFJ)V") \
     CALLBACK (handleMouseDragJni,          "handleMouseDrag",             "(JIFFJ)V") \
     CALLBACK (handleMouseUpJni,            "handleMouseUp",               "(JIFFJ)V") \
     CALLBACK (handleKeyDownJni,            "handleKeyDown",               "(JII)V") \
     CALLBACK (handleKeyUpJni,              "handleKeyUp",                 "(JII)V") \
     CALLBACK (handleBackButtonJni,         "handleBackButton",            "(J)V") \
     CALLBACK (handleKeyboardHiddenJni,     "handleKeyboardHidden",        "(J)V") \
     CALLBACK (viewSizeChangedJni,          "viewSizeChanged",             "(J)V") \
     CALLBACK (focusChangedJni,             "focusChanged",                "(JZ)V") \
     CALLBACK (handleAppPausedJni,          "handleAppPaused",             "(J)V") \
     CALLBACK (handleAppResumedJni,         "handleAppResumed",            "(J)V") \

    DECLARE_JNI_CLASS_WITH_BYTECODE (ComponentPeerView, "com/rmsl/juce/ComponentPeerView", 16, javaComponentPeerView, sizeof (javaComponentPeerView))
    #undef JNI_CLASS_MEMBERS

    static void JNICALL handlePaintJni          (JNIEnv*, jobject /*view*/, jlong host, jobject canvas, jobject paint)           { if (auto* myself = reinterpret_cast<AndroidComponentPeer*> (host)) myself->handlePaintCallback (canvas, paint); }
    static void JNICALL handleMouseDownJni      (JNIEnv*, jobject /*view*/, jlong host, jint i, jfloat x, jfloat y, jlong time)  { if (auto* myself = reinterpret_cast<AndroidComponentPeer*> (host)) myself->handleMouseDownCallback (i, Point<float> ((float) x, (float) y), (int64) time); }
    static void JNICALL handleMouseDragJni      (JNIEnv*, jobject /*view*/, jlong host, jint i, jfloat x, jfloat y, jlong time)  { if (auto* myself = reinterpret_cast<AndroidComponentPeer*> (host)) myself->handleMouseDragCallback (i, Point<float> ((float) x, (float) y), (int64) time); }
    static void JNICALL handleMouseUpJni        (JNIEnv*, jobject /*view*/, jlong host, jint i, jfloat x, jfloat y, jlong time)  { if (auto* myself = reinterpret_cast<AndroidComponentPeer*> (host)) myself->handleMouseUpCallback   (i, Point<float> ((float) x, (float) y), (int64) time); }
    static void JNICALL viewSizeChangedJni      (JNIEnv*, jobject /*view*/, jlong host)                                          { if (auto* myself = reinterpret_cast<AndroidComponentPeer*> (host)) myself->handleMovedOrResized(); }
    static void JNICALL focusChangedJni         (JNIEnv*, jobject /*view*/, jlong host, jboolean hasFocus)                       { if (auto* myself = reinterpret_cast<AndroidComponentPeer*> (host)) myself->handleFocusChangeCallback (hasFocus); }
    static void JNICALL handleKeyDownJni        (JNIEnv*, jobject /*view*/, jlong host, jint k, jint kc)                         { if (auto* myself = reinterpret_cast<AndroidComponentPeer*> (host)) myself->handleKeyDownCallback ((int) k, (int) kc); }
    static void JNICALL handleKeyUpJni          (JNIEnv*, jobject /*view*/, jlong host, jint k, jint kc)                         { if (auto* myself = reinterpret_cast<AndroidComponentPeer*> (host)) myself->handleKeyUpCallback ((int) k, (int) kc); }
    static void JNICALL handleBackButtonJni     (JNIEnv*, jobject /*view*/, jlong host)                                          { if (auto* myself = reinterpret_cast<AndroidComponentPeer*> (host)) myself->handleBackButtonCallback(); }
    static void JNICALL handleKeyboardHiddenJni (JNIEnv*, jobject /*view*/, jlong host)                                          { if (auto* myself = reinterpret_cast<AndroidComponentPeer*> (host)) myself->handleKeyboardHiddenCallback(); }
    static void JNICALL handleAppPausedJni      (JNIEnv*, jobject /*view*/, jlong host)                                          { if (auto* myself = reinterpret_cast<AndroidComponentPeer*> (host)) myself->handleAppPausedCallback(); }
    static void JNICALL handleAppResumedJni     (JNIEnv*, jobject /*view*/, jlong host)                                          { if (auto* myself = reinterpret_cast<AndroidComponentPeer*> (host)) myself->handleAppResumedCallback(); }

    //==============================================================================
    friend class Displays;
    static AndroidComponentPeer* frontWindow;
    static GlobalRef activityCallbackListener;

    //==============================================================================
    static constexpr int GRAVITY_LEFT = 0x3, GRAVITY_TOP = 0x30;
    static constexpr int TYPE_APPLICATION = 0x2;
    static constexpr int FLAG_NOT_TOUCH_MODAL = 0x20, FLAG_LAYOUT_IN_SCREEN = 0x100, FLAG_LAYOUT_NO_LIMITS = 0x200;
    static constexpr int PIXEL_FORMAT_OPAQUE = -1, PIXEL_FORMAT_TRANSPARENT = -2;

    struct PreallocatedImage  : public ImagePixelData
    {
        PreallocatedImage (int width_, int height_, jint* data_, bool hasAlpha_)
            : ImagePixelData (Image::ARGB, width_, height_), data (data_), hasAlpha (hasAlpha_)
        {
            if (hasAlpha_)
                zeromem (data_, static_cast<size_t> (width * height) * sizeof (jint));
        }

        ~PreallocatedImage() override
        {
            if (hasAlpha)
            {
                auto pix = (PixelARGB*) data;

                for (int i = width * height; --i >= 0;)
                {
                    pix->unpremultiply();
                    ++pix;
                }
            }
        }

        std::unique_ptr<ImageType> createType() const override
        {
            return std::make_unique<SoftwareImageType>();
        }

        std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() override
        {
            return std::make_unique<LowLevelGraphicsSoftwareRenderer> (Image (this));
        }

        void initialiseBitmapData (Image::BitmapData& bm, int x, int y, Image::BitmapData::ReadWriteMode /*mode*/) override
        {
            bm.lineStride = width * static_cast<int> (sizeof (jint));
            bm.pixelStride = static_cast<int> (sizeof (jint));
            bm.pixelFormat = Image::ARGB;
            bm.data = (uint8*) (data + x + y * width);
        }

        ImagePixelData::Ptr clone() override
        {
            auto s = new PreallocatedImage (width, height, nullptr, hasAlpha);
            s->allocatedData.malloc (sizeof (jint) * static_cast<size_t> (width * height));
            s->data = s->allocatedData;
            memcpy (s->data, data, sizeof (jint) * static_cast<size_t> (width * height));
            return s;
        }

    private:
        jint* data;
        HeapBlock<jint> allocatedData;
        bool hasAlpha;

        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PreallocatedImage)
    };

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidComponentPeer)
};

Point<float> AndroidComponentPeer::lastMousePos;
int64 AndroidComponentPeer::touchesDown = 0;
AndroidComponentPeer* AndroidComponentPeer::frontWindow = nullptr;
GlobalRef AndroidComponentPeer::activityCallbackListener;
AndroidComponentPeer::ComponentPeerView_Class AndroidComponentPeer::ComponentPeerView;

//==============================================================================
ComponentPeer* Component::createNewPeer (int styleFlags, void* nativeWindow)
{
    return new AndroidComponentPeer (*this, styleFlags, nativeWindow);
}

//==============================================================================
bool Desktop::canUseSemiTransparentWindows() noexcept
{
    return true;
}

double Desktop::getDefaultMasterScale()
{
    return 1.0;
}

Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
{
    enum
    {
        ROTATION_0   = 0,
        ROTATION_90  = 1,
        ROTATION_180 = 2,
        ROTATION_270 = 3
    };

    JNIEnv* env = getEnv();
    LocalRef<jstring> windowServiceString (javaString ("window"));


    LocalRef<jobject> windowManager = LocalRef<jobject> (env->CallObjectMethod (getAppContext().get(), AndroidContext.getSystemService, windowServiceString.get()));

    if (windowManager.get() != nullptr)
    {
        LocalRef<jobject> display = LocalRef<jobject> (env->CallObjectMethod (windowManager, AndroidWindowManager.getDefaultDisplay));

        if (display.get() != nullptr)
        {
            int rotation = env->CallIntMethod (display, AndroidDisplay.getRotation);

            switch (rotation)
            {
                case ROTATION_0:   return upright;
                case ROTATION_90:  return rotatedAntiClockwise;
                case ROTATION_180: return upsideDown;
                case ROTATION_270: return rotatedClockwise;
            }
        }
    }

    jassertfalse;
    return upright;
}

bool MouseInputSource::SourceList::addSource()
{
    addSource (sources.size(), MouseInputSource::InputSourceType::touch);
    return true;
}

bool MouseInputSource::SourceList::canUseTouch()
{
    return true;
}

Point<float> MouseInputSource::getCurrentRawMousePosition()
{
    return AndroidComponentPeer::lastMousePos;
}

void MouseInputSource::setRawMousePosition (Point<float>)
{
    // not needed
}

//==============================================================================
bool KeyPress::isKeyCurrentlyDown (int /*keyCode*/)
{
    // TODO
    return false;
}

JUCE_API void JUCE_CALLTYPE Process::hide()
{
    auto* env = getEnv();
    LocalRef<jobject> currentActivity (getCurrentActivity().get());

    if (env->CallBooleanMethod (currentActivity.get(), AndroidActivity.moveTaskToBack, true) == 0)
    {
        GlobalRef intent (LocalRef<jobject> (env->NewObject (AndroidIntent, AndroidIntent.constructor)));
        env->CallObjectMethod (intent, AndroidIntent.setAction,   javaString ("android.intent.action.MAIN")  .get());
        env->CallObjectMethod (intent, AndroidIntent.addCategory, javaString ("android.intent.category.HOME").get());

        env->CallVoidMethod (currentActivity.get(), AndroidContext.startActivity, intent.get());
    }
}

//==============================================================================
// TODO
JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess() { return true; }
JUCE_API void JUCE_CALLTYPE Process::makeForegroundProcess() {}

//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 METHOD (show,                   "show",                 "()V") \
 METHOD (getWindow,              "getWindow",            "()Landroid/view/Window;")

DECLARE_JNI_CLASS (AndroidDialog, "android/app/Dialog")
#undef JNI_CLASS_MEMBERS

#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 METHOD (construct,                   "<init>",                 "(Landroid/content/Context;)V") \
 METHOD (create,                      "create",                 "()Landroid/app/AlertDialog;") \
 METHOD (setTitle,                    "setTitle",               "(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;") \
 METHOD (setMessage,                  "setMessage",             "(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;") \
 METHOD (setCancelable,               "setCancelable",          "(Z)Landroid/app/AlertDialog$Builder;") \
 METHOD (setOnCancelListener,         "setOnCancelListener",    "(Landroid/content/DialogInterface$OnCancelListener;)Landroid/app/AlertDialog$Builder;") \
 METHOD (setPositiveButton,           "setPositiveButton",      "(Ljava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder;") \
 METHOD (setNegativeButton,           "setNegativeButton",      "(Ljava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder;") \
 METHOD (setNeutralButton,            "setNeutralButton",       "(Ljava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder;")

DECLARE_JNI_CLASS (AndroidAlertDialogBuilder, "android/app/AlertDialog$Builder")
#undef JNI_CLASS_MEMBERS

#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 METHOD (dismiss,    "dismiss",  "()V")

DECLARE_JNI_CLASS (AndroidDialogInterface, "android/content/DialogInterface")
#undef JNI_CLASS_MEMBERS

#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \

DECLARE_JNI_CLASS (AndroidDialogOnClickListener, "android/content/DialogInterface$OnClickListener")
#undef JNI_CLASS_MEMBERS

//==============================================================================
class DialogListener  : public juce::AndroidInterfaceImplementer
{
public:
    DialogListener (ModalComponentManager::Callback* callbackToUse, int resultToUse)
        : callback (callbackToUse), result (resultToUse)
    {}

    void onResult (jobject dialog)
    {
        auto* env = getEnv();
        env->CallVoidMethod (dialog, AndroidDialogInterface.dismiss);

        if (callback != nullptr)
            callback->modalStateFinished (result);

        callback = nullptr;
    }

private:
    jobject invoke (jobject proxy, jobject method, jobjectArray args) override
    {
        auto* env = getEnv();
        auto methodName = juce::juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));

        if (methodName == "onCancel" || methodName == "onClick")
        {
            onResult (env->GetObjectArrayElement (args, 0));
            return nullptr;
        }

        // invoke base class
        return AndroidInterfaceImplementer::invoke (proxy, method, args);
    }

    std::unique_ptr<ModalComponentManager::Callback> callback;
    int result;
};

//==============================================================================
static void createAndroidDialog (const String& title, const String& message,
                                 ModalComponentManager::Callback* callback,
                                 const String& positiveButton = {}, const String& negativeButton = {},
                                 const String& neutralButton = {})
{
    auto* env = getEnv();

    LocalRef<jobject> builder (env->NewObject (AndroidAlertDialogBuilder, AndroidAlertDialogBuilder.construct, getMainActivity().get()));

    builder = LocalRef<jobject> (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setTitle,   javaString (title).get()));
    builder = LocalRef<jobject> (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setMessage, javaString (message).get()));
    builder = LocalRef<jobject> (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setCancelable, true));

    builder = LocalRef<jobject> (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setOnCancelListener,
                                     CreateJavaInterface (new DialogListener (callback, 0),
                                                          "android/content/DialogInterface$OnCancelListener").get()));

    auto positiveButtonText = positiveButton.isEmpty() ? String ("OK") : positiveButton;

    builder = LocalRef<jobject> (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setPositiveButton,
                                     javaString (positiveButtonText).get(),
                                     CreateJavaInterface (new DialogListener (callback, positiveButton.isEmpty() ? 0 : 1),
                                                          "android/content/DialogInterface$OnClickListener").get()));

    if (negativeButton.isNotEmpty())
        builder = LocalRef<jobject> (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setNegativeButton,
                                         javaString (negativeButton).get(),
                                         CreateJavaInterface (new DialogListener (callback, neutralButton.isEmpty() ? 0 : 2),
                                                              "android/content/DialogInterface$OnClickListener").get()));

    if (neutralButton.isNotEmpty())
        builder = LocalRef<jobject> (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setNegativeButton,
                                         javaString (neutralButton).get(),
                                         CreateJavaInterface (new DialogListener (callback, 0),
                                                              "android/content/DialogInterface$OnClickListener").get()));

    LocalRef<jobject> dialog (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.create));

    LocalRef<jobject> window (env->CallObjectMethod (dialog.get(), AndroidDialog.getWindow));

    if (Desktop::getInstance().getKioskModeComponent() != nullptr)
    {
        env->CallVoidMethod (window.get(), AndroidWindow.setFlags, FLAG_NOT_FOCUSABLE, FLAG_NOT_FOCUSABLE);
        LocalRef<jobject> decorView (env->CallObjectMethod (window.get(), AndroidWindow.getDecorView));
        env->CallVoidMethod (decorView.get(), AndroidView.setSystemUiVisibility, fullScreenFlags);
    }

    env->CallVoidMethod (dialog.get(), AndroidDialog.show);

    if (Desktop::getInstance().getKioskModeComponent() != nullptr)
        env->CallVoidMethod (window.get(), AndroidWindow.clearFlags, FLAG_NOT_FOCUSABLE);
}

void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType /*iconType*/,
                                                          const String& title, const String& message,
                                                          Component* /*associatedComponent*/,
                                                          ModalComponentManager::Callback* callback)
{
    createAndroidDialog (title, message, callback);
}

bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType /*iconType*/,
                                                      const String& title, const String& message,
                                                      Component* /*associatedComponent*/,
                                                      ModalComponentManager::Callback* callback)
{
    jassert (callback != nullptr); // on android, all alerts must be non-modal!!

    createAndroidDialog (title, message, callback, "OK", "Cancel");
    return false;
}

int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType /*iconType*/,
                                                        const String& title, const String& message,
                                                        Component* /*associatedComponent*/,
                                                        ModalComponentManager::Callback* callback)
{
    jassert (callback != nullptr); // on android, all alerts must be non-modal!!

    createAndroidDialog (title, message, callback, "Yes", "No", "Cancel");
    return 0;
}

int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType /*iconType*/,
                                                   const String& title, const String& message,
                                                   Component* /*associatedComponent*/,
                                                   ModalComponentManager::Callback* callback)
{
    jassert (callback != nullptr); // on android, all alerts must be non-modal!!

    createAndroidDialog (title, message, callback, "Yes", "No");
    return 0;
}

//==============================================================================
static bool androidScreenSaverEnabled = true;

void Desktop::setScreenSaverEnabled (bool shouldEnable)
{
    constexpr auto FLAG_KEEP_SCREEN_ON = 0x80;

    if (shouldEnable != androidScreenSaverEnabled)
    {
        LocalRef<jobject> activity (getMainActivity());

        if (activity != nullptr)
        {
            auto* env = getEnv();

            LocalRef<jobject> mainWindow (env->CallObjectMethod (activity.get(), AndroidActivity.getWindow));
            env->CallVoidMethod (mainWindow.get(), AndroidWindow.setFlags, shouldEnable ? 0 : FLAG_KEEP_SCREEN_ON, FLAG_KEEP_SCREEN_ON);
        }

        androidScreenSaverEnabled = shouldEnable;
    }
}

bool Desktop::isScreenSaverEnabled()
{
    return androidScreenSaverEnabled;
}

//==============================================================================
void Desktop::setKioskComponent (Component* kioskComp, bool enableOrDisable, bool allowMenusAndBars)
{
    ignoreUnused (allowMenusAndBars);

    if (AndroidComponentPeer* peer = dynamic_cast<AndroidComponentPeer*> (kioskComp->getPeer()))
        peer->setFullScreen (enableOrDisable);
    else
        jassertfalse; // (this should have been checked by the caller)
}

//==============================================================================
static jint getAndroidOrientationFlag (int orientations) noexcept
{
    enum
    {
        SCREEN_ORIENTATION_LANDSCAPE          = 0,
        SCREEN_ORIENTATION_PORTRAIT           = 1,
        SCREEN_ORIENTATION_USER               = 2,
        SCREEN_ORIENTATION_REVERSE_LANDSCAPE  = 8,
        SCREEN_ORIENTATION_REVERSE_PORTRAIT   = 9,
        SCREEN_ORIENTATION_USER_LANDSCAPE     = 11,
        SCREEN_ORIENTATION_USER_PORTRAIT      = 12,
    };

    switch (orientations)
    {
        case Desktop::upright:                                          return (jint) SCREEN_ORIENTATION_PORTRAIT;
        case Desktop::upsideDown:                                       return (jint) SCREEN_ORIENTATION_REVERSE_PORTRAIT;
        case Desktop::upright + Desktop::upsideDown:                    return (jint) SCREEN_ORIENTATION_USER_PORTRAIT;
        case Desktop::rotatedAntiClockwise:                             return (jint) SCREEN_ORIENTATION_LANDSCAPE;
        case Desktop::rotatedClockwise:                                 return (jint) SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
        case Desktop::rotatedClockwise + Desktop::rotatedAntiClockwise: return (jint) SCREEN_ORIENTATION_USER_LANDSCAPE;
        default:                                                        return (jint) SCREEN_ORIENTATION_USER;
    }
}

void Desktop::allowedOrientationsChanged()
{
    LocalRef<jobject> activity (getMainActivity());

    if (activity != nullptr)
        getEnv()->CallVoidMethod (activity.get(), AndroidActivity.setRequestedOrientation, getAndroidOrientationFlag (allowedOrientations));
}

//==============================================================================
bool juce_areThereAnyAlwaysOnTopWindows()
{
    return false;
}

//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 METHOD (create,          "<init>",         "()V") \
 FIELD  (density,         "density",        "F") \
 FIELD  (widthPixels,     "widthPixels",    "I") \
 FIELD  (heightPixels,    "heightPixels",   "I")

DECLARE_JNI_CLASS (AndroidDisplayMetrics, "android/util/DisplayMetrics")
#undef JNI_CLASS_MEMBERS

//==============================================================================
class LayoutChangeListener  : public juce::AndroidInterfaceImplementer
{
public:
    virtual void onLayoutChange (LocalRef<jobject> view, int left, int top, int right, int bottom,
                                 int oldLeft, int oldTop, int oldRight, int oldBottom) = 0;

private:
    jobject invoke (jobject proxy, jobject method, jobjectArray args) override
    {
        auto* env = getEnv();
        auto methodName = juce::juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));

        if (methodName == "onLayoutChange")
        {
            jassert (env->GetArrayLength (args) == 9);

            LocalRef<jobject> view (env->GetObjectArrayElement (args, 0));
            int dims[8];

            for (int i = 1; i < 9; ++i)
            {
                LocalRef<jobject> integer (env->GetObjectArrayElement (args, i));
                dims[i - 1] = env->CallIntMethod (integer.get(), JavaInteger.intValue);
            }

            onLayoutChange (std::move (view), dims[0], dims[1], dims[2], dims[3],
                            dims[4], dims[5], dims[6], dims[7]);

            return nullptr;
        }

        // invoke base class
        return AndroidInterfaceImplementer::invoke (proxy, method, args);
    }

    std::unique_ptr<ModalComponentManager::Callback> callback;
};

//==============================================================================
class MainActivityWindowLayoutListener   : public LayoutChangeListener
{
public:
    void onLayoutChange (LocalRef<jobject> /*view*/, int left, int top, int right, int bottom,
                         int oldLeft, int oldTop, int oldRight, int oldBottom) override
    {
        auto newBounds = Rectangle<int>::leftTopRightBottom (left, top, right, bottom);
        auto oldBounds = Rectangle<int>::leftTopRightBottom (oldLeft, oldTop, oldRight, oldBottom);

        if (newBounds != oldBounds)
        {
            auto& displays = Desktop::getInstance().getDisplays();
            auto& mainDisplay = displays.getMainDisplay();

            Rectangle<int> userArea = newBounds / mainDisplay.scale;

            if (userArea != mainDisplay.userArea)
                const_cast<Displays&> (displays).refresh();
        }
    }
};

//==============================================================================
void Displays::findDisplays (float masterScale)
{
    auto* env = getEnv();

    LocalRef<jobject> usableSize (env->NewObject (AndroidPoint, AndroidPoint.create, 0, 0));
    LocalRef<jstring> windowServiceString (javaString ("window"));
    LocalRef<jobject> displayMetrics (env->NewObject (AndroidDisplayMetrics, AndroidDisplayMetrics.create));
    LocalRef<jobject> windowManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getSystemService, windowServiceString.get()));
    LocalRef <jobject> display (env->CallObjectMethod (windowManager, AndroidWindowManager.getDefaultDisplay));

    jmethodID getRealMetricsMethod = env->GetMethodID (AndroidDisplay, "getRealMetrics", "(Landroid/util/DisplayMetrics;)V");

    if (getRealMetricsMethod != nullptr)
        env->CallVoidMethod (display.get(), getRealMetricsMethod, displayMetrics.get());
    else
        env->CallVoidMethod (display.get(), AndroidDisplay.getMetrics, displayMetrics.get());

    env->CallVoidMethod (display.get(), AndroidDisplay.getSize, usableSize.get());

    Display d;

    d.isMain = true;
    d.scale = env->GetFloatField (displayMetrics.get(), AndroidDisplayMetrics.density);
    d.dpi = (d.scale * 160.f);
    d.scale *= masterScale;

    d.totalArea = Rectangle<int> (env->GetIntField (displayMetrics.get(), AndroidDisplayMetrics.widthPixels),
                                  env->GetIntField (displayMetrics.get(), AndroidDisplayMetrics.heightPixels)) / d.scale;

    d.userArea = Rectangle<int> (env->GetIntField (usableSize.get(), AndroidPoint.x),
                                 env->GetIntField (usableSize.get(), AndroidPoint.y)) / d.scale;

    // unfortunately usableSize still contains the nav bar
    // the best workaround is to try to get the size of the top-level view of
    // the main activity
    LocalRef<jobject> activity (getMainActivity());

    if (activity != nullptr)
    {
        LocalRef<jobject> mainWindow (env->CallObjectMethod (activity.get(), AndroidActivity.getWindow));
        LocalRef<jobject> decorView (env->CallObjectMethod (mainWindow.get(), AndroidWindow.getDecorView));
        LocalRef<jobject> contentView (env->CallObjectMethod (decorView.get(), AndroidView.findViewById, 0x01020002 /* android.R.id.content */));

        if (contentView != nullptr)
        {
            Rectangle<int> activityArea (env->CallIntMethod (contentView.get(), AndroidView.getLeft),
                                         env->CallIntMethod (contentView.get(), AndroidView.getTop),
                                         env->CallIntMethod (contentView.get(), AndroidView.getWidth),
                                         env->CallIntMethod (contentView.get(), AndroidView.getHeight));

            if (! activityArea.isEmpty())
                d.userArea = activityArea / d.scale;

            static bool hasAddedMainActivityListener = false;

            if (! hasAddedMainActivityListener)
            {
                hasAddedMainActivityListener = true;

                env->CallVoidMethod (contentView.get(), AndroidView.addOnLayoutChangeListener,
                                     CreateJavaInterface (new MainActivityWindowLayoutListener,
                                                          "android/view/View$OnLayoutChangeListener").get());
            }
        }
    }
    else
    {
        // the main activity may have not started yet so add an activity listener
        if (AndroidComponentPeer::activityCallbackListener == nullptr)
        {
            LocalRef<jobject> appContext (getAppContext());

            if (appContext.get() != nullptr)
            {
                AndroidComponentPeer::activityCallbackListener = GlobalRef (CreateJavaInterface (
                        new AndroidComponentPeer::StartupActivityCallbackListener,
                        "android/app/Application$ActivityLifecycleCallbacks"));

                env->CallVoidMethod (appContext.get(),
                                     AndroidApplication.registerActivityLifecycleCallbacks,
                                     AndroidComponentPeer::activityCallbackListener.get());
            }
        }
    }

    displays.add (d);
}

//==============================================================================
Image juce_createIconForFile (const File& /*file*/)
{
    return Image();
}

//==============================================================================
void* CustomMouseCursorInfo::create() const                                         { return nullptr; }
void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType)      { return nullptr; }
void MouseCursor::deleteMouseCursor (void* /*cursorHandle*/, bool /*isStandard*/)   {}

//==============================================================================
void MouseCursor::showInWindow (ComponentPeer*) const   {}

//==============================================================================
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& /*files*/, bool /*canMove*/,
                                                           Component* /*srcComp*/, std::function<void()> /*callback*/)
{
    jassertfalse;    // no such thing on Android!
    return false;
}

bool DragAndDropContainer::performExternalDragDropOfText (const String& /*text*/, Component* /*srcComp*/,
                                                          std::function<void()> /*callback*/)
{
    jassertfalse;    // no such thing on Android!
    return false;
}

//==============================================================================
void LookAndFeel::playAlertSound()
{
}

//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 METHOD (getText,      "getText",            "()Ljava/lang/CharSequence;") \
 METHOD (setText,      "setText",            "(Ljava/lang/CharSequence;)V")

DECLARE_JNI_CLASS (AndroidClipboardManager, "android/content/ClipboardManager")
#undef JNI_CLASS_MEMBERS

//==============================================================================
void SystemClipboard::copyTextToClipboard (const String& text)
{
    auto* env = getEnv();

    LocalRef<jobject> clipboardManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getSystemService, javaString ("clipboard").get()));
    env->CallVoidMethod (clipboardManager.get(), AndroidClipboardManager.setText, javaString(text).get());
}

String SystemClipboard::getTextFromClipboard()
{
    auto* env = getEnv();

    LocalRef<jobject> clipboardManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getSystemService, javaString ("clipboard").get()));
    LocalRef<jobject> charSequence (env->CallObjectMethod (clipboardManager.get(), AndroidClipboardManager.getText));

    if (charSequence == nullptr)
        return {};

    return juceString(LocalRef<jstring> ((jstring) env->CallObjectMethod(charSequence.get(), JavaCharSequence.toString)));
}

//==============================================================================
const int extendedKeyModifier       = 0x10000;

const int KeyPress::spaceKey        = ' ';
const int KeyPress::returnKey       = 66;
const int KeyPress::escapeKey       = 4;
const int KeyPress::backspaceKey    = 67;
const int KeyPress::leftKey         = extendedKeyModifier + 1;
const int KeyPress::rightKey        = extendedKeyModifier + 2;
const int KeyPress::upKey           = extendedKeyModifier + 3;
const int KeyPress::downKey         = extendedKeyModifier + 4;
const int KeyPress::pageUpKey       = extendedKeyModifier + 5;
const int KeyPress::pageDownKey     = extendedKeyModifier + 6;
const int KeyPress::endKey          = extendedKeyModifier + 7;
const int KeyPress::homeKey         = extendedKeyModifier + 8;
const int KeyPress::deleteKey       = extendedKeyModifier + 9;
const int KeyPress::insertKey       = -1;
const int KeyPress::tabKey          = 61;
const int KeyPress::F1Key           = extendedKeyModifier + 10;
const int KeyPress::F2Key           = extendedKeyModifier + 11;
const int KeyPress::F3Key           = extendedKeyModifier + 12;
const int KeyPress::F4Key           = extendedKeyModifier + 13;
const int KeyPress::F5Key           = extendedKeyModifier + 14;
const int KeyPress::F6Key           = extendedKeyModifier + 16;
const int KeyPress::F7Key           = extendedKeyModifier + 17;
const int KeyPress::F8Key           = extendedKeyModifier + 18;
const int KeyPress::F9Key           = extendedKeyModifier + 19;
const int KeyPress::F10Key          = extendedKeyModifier + 20;
const int KeyPress::F11Key          = extendedKeyModifier + 21;
const int KeyPress::F12Key          = extendedKeyModifier + 22;
const int KeyPress::F13Key          = extendedKeyModifier + 23;
const int KeyPress::F14Key          = extendedKeyModifier + 24;
const int KeyPress::F15Key          = extendedKeyModifier + 25;
const int KeyPress::F16Key          = extendedKeyModifier + 26;
const int KeyPress::F17Key          = extendedKeyModifier + 50;
const int KeyPress::F18Key          = extendedKeyModifier + 51;
const int KeyPress::F19Key          = extendedKeyModifier + 52;
const int KeyPress::F20Key          = extendedKeyModifier + 53;
const int KeyPress::F21Key          = extendedKeyModifier + 54;
const int KeyPress::F22Key          = extendedKeyModifier + 55;
const int KeyPress::F23Key          = extendedKeyModifier + 56;
const int KeyPress::F24Key          = extendedKeyModifier + 57;
const int KeyPress::F25Key          = extendedKeyModifier + 58;
const int KeyPress::F26Key          = extendedKeyModifier + 59;
const int KeyPress::F27Key          = extendedKeyModifier + 60;
const int KeyPress::F28Key          = extendedKeyModifier + 61;
const int KeyPress::F29Key          = extendedKeyModifier + 62;
const int KeyPress::F30Key          = extendedKeyModifier + 63;
const int KeyPress::F31Key          = extendedKeyModifier + 64;
const int KeyPress::F32Key          = extendedKeyModifier + 65;
const int KeyPress::F33Key          = extendedKeyModifier + 66;
const int KeyPress::F34Key          = extendedKeyModifier + 67;
const int KeyPress::F35Key          = extendedKeyModifier + 68;
const int KeyPress::numberPad0      = extendedKeyModifier + 27;
const int KeyPress::numberPad1      = extendedKeyModifier + 28;
const int KeyPress::numberPad2      = extendedKeyModifier + 29;
const int KeyPress::numberPad3      = extendedKeyModifier + 30;
const int KeyPress::numberPad4      = extendedKeyModifier + 31;
const int KeyPress::numberPad5      = extendedKeyModifier + 32;
const int KeyPress::numberPad6      = extendedKeyModifier + 33;
const int KeyPress::numberPad7      = extendedKeyModifier + 34;
const int KeyPress::numberPad8      = extendedKeyModifier + 35;
const int KeyPress::numberPad9      = extendedKeyModifier + 36;
const int KeyPress::numberPadAdd            = extendedKeyModifier + 37;
const int KeyPress::numberPadSubtract       = extendedKeyModifier + 38;
const int KeyPress::numberPadMultiply       = extendedKeyModifier + 39;
const int KeyPress::numberPadDivide         = extendedKeyModifier + 40;
const int KeyPress::numberPadSeparator      = extendedKeyModifier + 41;
const int KeyPress::numberPadDecimalPoint   = extendedKeyModifier + 42;
const int KeyPress::numberPadEquals         = extendedKeyModifier + 43;
const int KeyPress::numberPadDelete         = extendedKeyModifier + 44;
const int KeyPress::playKey         = extendedKeyModifier + 45;
const int KeyPress::stopKey         = extendedKeyModifier + 46;
const int KeyPress::fastForwardKey  = extendedKeyModifier + 47;
const int KeyPress::rewindKey       = extendedKeyModifier + 48;

} // namespace juce
