bsimic
bsimic

Reputation: 926

Android wear is not inflating the proper layout for custom watch face on round screen

I am making a custom watch face for Android Wear. I am having an issue with the watch face not being inflated correctly on emulators and devices with a round screen. I am able to load the round layout successfully on other Activities on the device but it is not working for the watch face activity itself.

This is my manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.aegiswallet"
    android:versionCode="1"
    android:versionName="1.0.0">

    <uses-feature android:name="android.hardware.type.watch" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/icon"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.DeviceDefault"
        android:name=".application.AegisWearApplication">

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        <activity
            android:name=".activities.MyActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
            android:name=".activities.WatchFaceActivity"
            android:allowEmbedded="true"
            android:label="Aegis Wallet">

            <meta-data
                android:name="com.google.android.clockwork.home.preview"
                android:resource="@drawable/watchface" />

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="com.google.android.clockwork.home.category.HOME_BACKGROUND" />
            </intent-filter>
        </activity>

        <activity android:name=".activities.ReceiveBitcoin" android:label="@string/receive_bitcoin"></activity>
        <service android:name=".services.MessageListenService">
            <intent-filter>
                <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
            </intent-filter>
        </service>

    </application>
</manifest>

The activity "MyActivity" successfully inflates the round layout but the "WatchFaceActivity" does not. The project can be found on GitHub here: https://github.com/bsimic0001/AegisWallet/

Below is the code for each:

public class MyActivity extends Activity implements SimpleGestureFilter.SimpleGestureListener {

    private SimpleGestureFilter detector;
    private TextView mTextView;
    private TextView balanceView;

    private static final int SPEECH_REQUEST_CODE = 0;
    private QRCodeWriter qrCodeWriter;
    private SharedPreferences prefs;
    private String address;
    private ImageView addressImageView;
    private Context context = this;

    private int STATE_QR = 100;
    private int STATE_BAL = 101;
    private int CURRENT_STATE = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_my);
        WatchViewStub stub = new WatchViewStub(this);

        stub.setRectLayout(R.layout.rect_activity_my);
        stub.setRoundLayout(R.layout.round_activity_my);

        detector = new SimpleGestureFilter(this, this);

        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {

                View rootView = stub.getRootView();
                mTextView = (TextView) stub.findViewById(R.id.text);
                addressImageView = (ImageView) stub.findViewById(R.id.address_image);
                balanceView = (TextView) stub.findViewById(R.id.balance_text);

                prefs = PreferenceManager.getDefaultSharedPreferences(context);
                address = prefs.getString("ADDRESS", null);

                if (address != null && addressImageView != null) {

                    qrCodeWriter = new QRCodeWriter();
                    Bitmap addressBitmap = encodeAsBitmap(address, BarcodeFormat.QR_CODE, 200);
                    addressImageView.setImageBitmap(addressBitmap);

                    addressImageView.setVisibility(View.VISIBLE);
                    CURRENT_STATE = STATE_QR;

                } else {
                    Log.d("MAINACTIVITY", "address image view must be null");
                }

                rootView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
                    @Override
                    public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {

                        final boolean round = insets.isRound();
                        Log.d("MainActivity", "Is the screen round: " + round);
                        return insets;
                    }
                });

            }
        });

        setContentView(stub);

        //displaySpeechRecognizer();
    }

    // Create an intent that can start the Speech Recognizer activity
    private void displaySpeechRecognizer() {
        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        // Start the activity, the intent will be populated with the speech text
        startActivityForResult(intent, SPEECH_REQUEST_CODE);
    }

    // This callback is invoked when the Speech Recognizer returns.
    // This is where you process the intent and extract the speech text from the intent.
    @Override
    protected void onActivityResult(int requestCode, int resultCode,
                                    Intent data) {
        if (requestCode == SPEECH_REQUEST_CODE && resultCode == RESULT_OK) {
            List<String> results = data.getStringArrayListExtra(
                    RecognizerIntent.EXTRA_RESULTS);
            String spokenText = results.get(0);

            Log.d("Main", "spoken text: " + spokenText);
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    public Bitmap encodeAsBitmap(String contents, BarcodeFormat format, int dimension) {

        Bitmap bitmap = null;

        try {
            final Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
            hints.put(EncodeHintType.MARGIN, 1);
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
            final BitMatrix result = qrCodeWriter.encode(contents, BarcodeFormat.QR_CODE, dimension, dimension, hints);

            final int width = result.getWidth();
            final int height = result.getHeight();
            final int[] pixels = new int[width * height];

            for (int y = 0; y < height; y++) {
                final int offset = y * width;
                for (int x = 0; x < width; x++) {
                    pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
                }
            }

            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
            return bitmap;

        } catch (WriterException e) {
            Log.e("Basic Utils", "cannot write to bitmap " + e.getMessage());
        }
        return bitmap;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent me) {
        // Call onTouchEvent of SimpleGestureFilter class
        this.detector.onTouchEvent(me);
        return super.dispatchTouchEvent(me);
    }

    @Override
    public void onSwipe(int direction) {
        String str = "";

        switch (direction) {

            case SimpleGestureFilter.SWIPE_RIGHT:
                str = "Swipe Right";
                break;
            case SimpleGestureFilter.SWIPE_LEFT:
                str = "Swipe Left";
                break;
            case SimpleGestureFilter.SWIPE_DOWN:
                str = "Swipe Down";
                handleSwipe(SimpleGestureFilter.SWIPE_DOWN);
                break;
            case SimpleGestureFilter.SWIPE_UP:
                str = "Swipe Up";
                handleSwipe(SimpleGestureFilter.SWIPE_UP);
                break;

        }
    }

    @Override
    public void onDoubleTap() {
       Log.d("MyActivity", "Double Tap");
    }

    private void handleSwipe(int swipeType){

        if(CURRENT_STATE == STATE_QR){
            addressImageView.setVisibility(View.GONE);
            balanceView.setVisibility(View.VISIBLE);
            CURRENT_STATE = STATE_BAL;

            balanceView.setText("WALLET BALANCE:\n" + prefs.getString("BALANCE", "Balance not Synced"));
        }
        else if(CURRENT_STATE == STATE_BAL){
            addressImageView.setVisibility(View.VISIBLE);
            balanceView.setVisibility(View.GONE);
            CURRENT_STATE = STATE_QR;
        }


    }
}

public class WatchFaceActivity extends Activity {

    private final static IntentFilter intentFilter;
    private boolean isDimmed = false;

    private String TAG = "AegisWearWatchFace";

    private Handler mHandler;

    TextView time;
    TextView timeAmPm;
    TextView btcValue;
    TextView walletBalance;

    SharedPreferences prefs;

    static {
        intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_TIME_TICK);
        intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
        intentFilter.addAction(Intent.ACTION_TIME_CHANGED);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.d(TAG, "inside the watch face activity");
        WatchViewStub stub = new WatchViewStub(this);
        stub.setRectLayout(R.layout.rect_activity_watch_face);
        stub.setRoundLayout(R.layout.round_activity_watch_face);

        stub.requestApplyInsets();


        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {

                Log.d(TAG, "Layout is inflated!");

                time = (TextView) stub.findViewById(R.id.time);
                timeAmPm = (TextView) stub.findViewById(R.id.time_ampm);
                btcValue = (TextView) stub.findViewById(R.id.bitcoin_value);
                walletBalance = (TextView) stub.findViewById(R.id.wallet_balance);

                View rootView = stub.getRootView();

                rootView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
                    @Override
                    public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
                        final boolean round = insets.isRound();
                        Log.d("WatchFaceActivity", "Is the screen round: " + round);
                        return insets;
                    }
                });

            }
        });



        setContentView(stub);

        prefs = PreferenceManager.getDefaultSharedPreferences(this);

        timeInfoReceiver.onReceive(this, registerReceiver(null, intentFilter));
        registerReceiver(timeInfoReceiver, intentFilter);
    }

    public BroadcastReceiver timeInfoReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context arg0, Intent intent) {
            Log.v("WatchFace", "timeChanged();");
            updateLayout();
        }
    };


    @Override
    protected void onPause() {
        super.onPause();
        isDimmed = true;
        Log.d(TAG, "dimmed");
        updateLayout();
    }

    @Override
    protected void onResume() {
        super.onResume();
        isDimmed = false;
        Log.d(TAG, "not dimmed");
        updateLayout();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(timeInfoReceiver);
    }

    public void updateLayout() {

        Calendar calendar = Calendar.getInstance();

        int hour = calendar.get(Calendar.HOUR);
        int minute = calendar.get(Calendar.MINUTE);
        String am_pm;

        if(calendar.get(Calendar.AM_PM) == 0)   am_pm = "AM";   else   am_pm = "PM";

        String btcValueString = prefs.getString("BTCAMOUNT", "");

        String hourText = hour + "";

        if(hour == 0)
           hour = 12;

        if(hour < 10)
            hourText = "0" + hourText;

        String minuteText = minute + "";
        if(minute < 10)
            minuteText = "0" + minuteText;

        if(time != null) {
            time.setText(hourText + ":" + minuteText);
            timeAmPm.setText(am_pm);
            btcValue.setText(btcValueString);
            walletBalance.setText(prefs.getString("BALANCE", ""));
        }
        else {
            Log.d("WatchFace", "time is null for some reason...");
        }

    }
}

Upvotes: 1

Views: 843

Answers (2)

jam0ral3s
jam0ral3s

Reputation: 544

Until the new andrioid smartwatch SDK is realeased, you can't use setOnApplyWindowInsetsListener / onApplyWindowInsets on custom watch face. This functionality only works on smartwatch app's.

To know if the clock face is round, you can use:

public static boolean heightSameAsWidth(Context context) {
    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();

    DisplayMetrics metrics = new DisplayMetrics();
    display.getMetrics(metrics);

    int width = metrics.widthPixels;
    int height = metrics.heightPixels;

    return height == width;
}

private void checkIfWatchIsRound() {
    if (heightSameAsWidth(getApplicationContext())) {
        isRound = false;
    } else {
        isRound = true;
    }
}

Upvotes: 0

dljava
dljava

Reputation: 1856

Try setting stub.onApplyWindowInsets(insets) inside your onApplyWindowInsets() method.

Upvotes: 1

Related Questions